71

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>
pmartin8
  • 1,545
  • 1
  • 20
  • 36
Rosh
  • 711
  • 1
  • 5
  • 4
  • Why is it not a good practice to use spring beans as static fields? – user59290 May 13 '20 at 09:42
  • @user59290: because static fields aren't under spring's control, they are subject to the classloader. spring can't tear down classes similarly to how it manages objects. – Nathan Hughes May 28 '20 at 14:44

7 Answers7

74

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

nullPainter
  • 2,676
  • 3
  • 22
  • 42
33

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.

František Hartman
  • 14,436
  • 2
  • 40
  • 60
  • 1
    Yes, I think we should use it as a parameter instead of setting directly in the static class. Based on the standards it says that we should not use the bean as a static though we can do that in different ways as shown in above responses. Thanks for the reply. – Rosh Sep 23 '12 at 14:16
  • 3
    "The result of static methods should depend ONLY on the parameters passed into the method", nice shot! – Nickolas Feb 10 '14 at 09:46
25

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

}
white
  • 1,823
  • 2
  • 12
  • 20
  • 2
    at first glance this is good idea, but `this.ac = ac;` is not right. It should be `TestUtils.ac = ac; `. Or you can define different name for `private static ApplicationContext utilAc`, then `utilAc = ac;`. – kkkkkk May 27 '20 at 08:31
  • Could ac be null, and cause NPE problem? – Xiaokun Nov 16 '20 at 15:35
15

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

Accessing spring bean without autowired

Is not related to the question, but if someone needs to access to a specific bean without the recommended @Autowired, you could use this:

@Autowired
private ApplicationContext appContext;


LdapConnector ldapConnector = (LdapConnector) appContext.getBean("myFooBean");

And in the entrypoint

  @Bean(name = "myFooBean")
  public LdapConnector myFooBeanInit() throws Exception {
    LdapConnector ldapFooConnector = new LdapConnectorImpl();
    ldapAcademicConnector.enableVerboseLog();
    ldapAcademicConnector.addCertToTrueStore();
    ldapAcademicConnector.disableSanMatch();
E_net4
  • 27,810
  • 13
  • 101
  • 139
JRichardsz
  • 14,356
  • 6
  • 59
  • 94
9

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;
    }
}
yngwietiger
  • 1,044
  • 1
  • 11
  • 15
1

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.

Andrei Sfat
  • 8,440
  • 5
  • 49
  • 69
0

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

Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125