Accessing spring beans in static method

JavaSpring

Java Problem Overview


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 use spring beans as static fields. But is there any way to access spring beans in a static method?

My example:

public class TestUtils {

   private static TestBean testBean;

   public void setTestBean(TestBean testBean) {
     TestUtils.testBean = testBean;
   }

  public static String getBeanDetails() {
    return beanName = testBean.getDetails();
  }
}

I have seen in many forums that this is not a best practice. Can someone show me how I can handle this type of scenario?

My configuration file:

<bean id="testUtils" class="com.test.TestUtils">
 <property name="testBean" ref="testBean" />
</bean>

Java Solutions


Solution 1 - Java

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).

Solution 2 - Java

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.

Solution 3 - Java

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;
  }

}

Solution 4 - Java

This worked for me.

Define your bean using xml configuration (old school):

<bean id="someBean1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName"><value>${db.driver}</value></property>		
	<property name="url"><value>${db.url}</value></property>
	<property name="username"><value>${db.username_seg}</value></property>
	<property name="password"><value>${db.password_seg}</value></property>
</bean>	

Or define it with java instead xml (new school)

@Bean(name = "someBean2")
public MySpringComponent loadSomeSpringComponent() {
  
  MySpringComponent bean = new MySpringComponent();
  bean.setSomeProperty("1.0.2");
  return bean;
}

Accessing spring bean in static method

import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

public class TestUtils {

  public static void getBeansFromSpringContext() {
    WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
    //for spring boot apps
    //ApplicationContext context = SpringApplication.run(Application.class, args)
    DataSource datasource  = (DataSource)context.getBean("someBean1");
    MySpringComponent springBean  = (MySpringComponent)context.getBean("someBean2");
  }
}	

HTH

Solution 5 - Java

Note: This is not how Spring is intended to be used and will have serious side-effects (difficult bugs to resolve, etc). Please try to refactor your code to not need this instead!

Generic Solution

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/

Usage

UserRepository userRepo = StaticContextAccessor.getBean(UserRespository.class)

Full code of the StaticContextAccessor:

@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);
        }
    }
}

Solution 6 - Java

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;
    }
}

Solution 7 - Java

This is how I injected from spring for a static field.

<bean id="..." class="...">
 <property name="fieldToBeInjected">
            <util:constant static-field="CONSTANT_FIELD" />
        </property>
</bean>

Maybe this will help you, as well.

Solution 8 - Java

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRoshView Question on Stackoverflow
Solution 1 - JavanullPainterView Answer on Stackoverflow
Solution 2 - JavaFrantišek HartmanView Answer on Stackoverflow
Solution 3 - JavawhiteView Answer on Stackoverflow
Solution 4 - JavaJRichardszView Answer on Stackoverflow
Solution 5 - JavaTom CoolsView Answer on Stackoverflow
Solution 6 - JavayngwietigerView Answer on Stackoverflow
Solution 7 - JavaAndrei SfatView Answer on Stackoverflow
Solution 8 - JavaBiju KunjummenView Answer on Stackoverflow