4

What are alternatives to @Autowire for static fields?

I went through many questions and understood why @Autowired can't be used with static fields. The accepted answer of this question mentions

You'll have to write your own logic to do this., as @Autowired cannot be used.

As I am new to dependency injection, I did not get what steps would have to be followed to write our own logic to do this.

What I got is that new cannot be used to create the object as it would tightly couple the 2 classes and DI is meant to be a solution for it. Also using @autowired with setters is not recommended in the second answer of the same question. So what are the alternatives to achieve same effect without using @Autowire ?

Community
  • 1
  • 1
Anubha
  • 1,345
  • 6
  • 23
  • 35
  • As mentioned in other answers you can put autowire on a non-static method and modify desired static filed inside it. Which is simpler than creating custom annotation. Link : http://www.connorgarvey.com/blog/?p=105 – Ajinkya Jan 02 '14 at 07:39
  • But this is same as using @autowired with setters, which is not recommended in second answer of the same question which I linked, also in link you gave it is mentioned " Avoid using this technique whenever possible" Is there no more acceptable solution ? – Anubha Jan 02 '14 at 07:42
  • btw part of the benefit of DI is you don't need static fields. spring doesn't knock itself out supporting this kind of thing because using spring (where you can specify scope declaratively) should remove the need to perpetrate this antipattern. – Nathan Hughes Jun 11 '14 at 13:25

4 Answers4

6

I will give you two alternatives. The first one are currently in a production environment. The second alternative is something that I came up with after thinking about this problem once more.

Still, this should be avoided, but if you have code that currently does not know about Spring, this can be used.

Alternative 1

Implement ApplicationContextAware
I had a situation where we needed to autowire a static field. I am working with legacy code where we want to use spring and jpa (hibernate) in our new code. In general it is bad practice to autowire static fields, but I belive that it can be motivated when considering the greater good for the application.

We solved this situation by implementing ApplicationContextAware. Se blelow:

public class DbHandler implements ApplicationContextAware {

private static DataSource dataSource;

protected DbHandler(){
    //Needs to be protected (not private) for Spring
}

//This method is used in many places in the code, and we can not change how it is 
//used. We wanted to use a datasource from Spring
public static Connection getConnection() throws java.sql.SQLException {
    return dataSource.getConnection();

}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    DbHandler.dataSource = applicationContext.getBean("dataSource", javax.sql.DataSource.class);
}

}

And since the class is not in a package that is scanned by spring we added it to the context.xml

<bean id="dbHandler" class="se.ucr.db.DbHandler" depends-on="dataSource"/>

This is not as easy as easy as writing @Autowired, but it works.

Alternative 2

Create a singelton

A bean that can be injected:

@Component
public class BeanToInject {

    private static final Logger LOGGER = LoggerFactory.getLogger(BeanToInject.class);

    public String getValue() {
        return "Value from SpringBean";
    }
}

A simple Singelton that will inject the bean that is defined above:

public class NeedStaticFieldInjected {

    private static final Logger LOGGER = LoggerFactory.getLogger(NeedStaticFieldInjected.class);

    private static NeedStaticFieldInjected INSTANCE;

    private NeedStaticFieldInjected() {
        //private constructor
    }

    @Autowired
    private BeanToInject beanToInject;

    public static NeedStaticFieldInjected getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new NeedStaticFieldInjected();
        }
        return INSTANCE;
    }


    public static String getValueFromSpringBean() {
        if (INSTANCE.beanToInject  == null)
            return "Not initialized correctly";

        return INSTANCE.beanToInject.getValue();
    }

}

And finally, the spring-context:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="org.emancipering.components"/>


<bean id="needStaticFieldInjected" class="org.emancipering.components.NeedStaticFieldInjected" factory-method="getInstance" />

</beans>

You will access the autowired properties via the static variable INSTANCE, as shown in NeedStaticFieldInjected .getValueFromSpringBean(). This is very close to the aim of injecting a static field. Of course, it is possible to have many more Autowired properties, but I just added one in this example.

It is important that factory-method="getInstance" is specified, otherwise this will fail.

I hope this is useful for someone.

Community
  • 1
  • 1
emanciperingsivraren
  • 1,215
  • 2
  • 15
  • 27
3

Well there is the "solution" for that: don't do it. Without seeing code sample from you I would say, why do you create something "static" if it depends on something dynamic (injected)?

Normally I would reconsider the responsibilities. Relocate the possible static part to a static util class. For the part that needs injections, create a normal service instead.

(And yes, there are always workarounds, but when you need workarounds you often should think for a better solution instead).

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
vertti
  • 7,539
  • 4
  • 51
  • 81
1

I need to integrate with an existing class's functionality(obfuscation) in a logging class which has all static methods. This logging class's static methods were used from 1000's of places. So the only way I can implement this is to create an instance of obfuscating class and assign the reference to the static variable and refer the static variable. It worked!

private static LogObfuscator staticObfuscator;

@Resource(name = "domain.logging.Obfuscator")
private  LogObfuscator obfuscatorInstance;

@PostConstruct
public void init() {
    staticObfuscator= obfuscatorInstance;
}
sivakadi
  • 181
  • 1
  • 4
0

You can create a bean that triggers a method everytime the context is refreshed (ie completed). In that method you'll set any static fields you like, as an example this would inject two beans into each other, even if they are static.

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

public class ApplicationListenerBean implements ApplicationListener {

    @Autowired
    SomeBean one;

    @Autowired
    SomeOtherBeab two;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            one.setTwo(two);
            two.setOne(one);
        }
    }
}
Andreas Wederbrand
  • 38,065
  • 11
  • 68
  • 78