I have a Util class with static methods. Inside my Util class, I want to use spring beans so I included them in my util class. As far as I know it\'s not a good practice to
The approach you have outlined is what I have seen being used to inject a Spring bean into a utility class.
<bean id="testUtils" class="com.test.TestUtils">
<property name="testBean" ref="testBean" />
</bean>
Another option is:
<bean name="methodInvokingFactoryBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="TestUtils.setInstance"/>
<property name="arguments">
<list>
<ref bean="testBean"/>
</list>
</property>
</bean>
with:
public class TestUtils {
private static testBean;
public static void setInstance(TestBean anInstance) {
testBean = anInstance;
}
public static String getBeanDetails() {
return testBean.getDetails();
}
}
More details are here and here
My approach is for the bean one wishes to access to implement InitializingBean
or use @PostConstruct
, and containing a static reference to itself.
For example:
@Service
public class MyBean implements InitializingBean {
private static MyBean instance;
@Override
public void afterPropertiesSet() throws Exception {
instance = this;
}
public static MyBean get() {
return instance;
}
}
Usage in your static class would therefore just be:
MyBean myBean = MyBean.get();
This way, no XML configuration is required, you don't need to pass the bean in as a constructor argument, and the caller doesn't need to know or care that the bean is wired up using Spring (i.e., no need for messy ApplicationContext
variables).
you may also implement ApplicationContextAware interface, like this:
@Component
public class TestUtils implements ApplicationContextAware {
private static ApplicationContext ac;
public static String getBeanDetails() {
return beanName = ((TestBean) ac.getBean("testBean")).getDetails();
}
@Override
public void setApplicationContext(ApplicationContext ac) {
TestUtils.ac = ac;
}
}
The result of static methods should depend ONLY on the parameters passed into the method, therefore there is no need to call any bean.
If you need to call another bean then your method should be a member method of a standalone bean.
Other answers give you working solutions, but the fact it can be done doesn't mean that it should be done.
Similar to @nullPainter's response, but we did the following. No post-construct logic required. It just sets the static member directly during injection (in the @Autowired method).
@Service
public class MyUtil {
private static MyManager myManager;
@Autowired(required = true)
public void setMyManager(MyManager manager) {
myManager = manager;
}
public static MyManager getMyManager() {
return myManager;
}
}
You can create a class that will allow access to any Bean from a static context. Most other answers here only show how to access a single class statically.
The Proxy in the code below was added in case someone calls the getBean() method before the ApplicationContext is autowired (as this would result in a nullpointer). None of the other solutions posted here handle that nullpointer.
Details on my blog: https://tomcools.be/post/apr-2020-static-spring-bean/
UserRepository userRepo = StaticContextAccessor.getBean(UserRespository.class)
@Component
public class StaticContextAccessor {
private static final Map<Class, DynamicInvocationhandler> classHandlers = new HashMap<>();
private static ApplicationContext context;
@Autowired
public StaticContextAccessor(ApplicationContext applicationContext) {
context = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
if (context == null) {
return getProxy(clazz);
}
return context.getBean(clazz);
}
private static <T> T getProxy(Class<T> clazz) {
DynamicInvocationhandler<T> invocationhandler = new DynamicInvocationhandler<>();
classHandlers.put(clazz, invocationhandler);
return (T) Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[]{clazz},
invocationhandler
);
}
//Use the context to get the actual beans and feed them to the invocationhandlers
@PostConstruct
private void init() {
classHandlers.forEach((clazz, invocationHandler) -> {
Object bean = context.getBean(clazz);
invocationHandler.setActualBean(bean);
});
}
static class DynamicInvocationhandler<T> implements InvocationHandler {
private T actualBean;
public void setActualBean(T actualBean) {
this.actualBean = actualBean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (actualBean == null) {
throw new RuntimeException("Not initialized yet! :(");
}
return method.invoke(actualBean, args);
}
}
}