@Autowired and static method

JavaSpringStaticAutowired

Java Problem Overview


I have @Autowired service which has to be used from within a static method. I know this is wrong but I cannot change the current design as it would require a lot of work, so I need some simple hack for that. I can't change randomMethod() to be non-static and I need to use this autowired bean. Any clues how to do that?

@Service
public class Foo {
    public int doStuff() {
        return 1;
    }
}

public class Boo {
    @Autowired
    Foo foo;
    
    public static void randomMethod() {
         foo.doStuff();
    }
}

Java Solutions


Solution 1 - Java

You can do this by following one of the solutions:

Using constructor @Autowired

This approach will construct the bean requiring some beans as constructor parameters. Within the constructor code you set the static field with the value got as parameter for constructor execution. Sample:

@Component
public class Boo {

    private static Foo foo;

    @Autowired
    public Boo(Foo foo) {
        Boo.foo = foo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

Using @PostConstruct to hand value over to static field

The idea here is to hand over a bean to a static field after bean is configured by spring.

@Component
public class Boo {

    private static Foo foo;
    @Autowired
    private Foo tFoo;

    @PostConstruct
    public void init() {
        Boo.foo = tFoo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

Solution 2 - Java

You have to workaround this via static application context accessor approach:

@Component
public class StaticContextAccessor {

    private static StaticContextAccessor instance;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void registerInstance() {
        instance = this;
    }

    public static <T> T getBean(Class<T> clazz) {
        return instance.applicationContext.getBean(clazz);
    }

}

Then you can access bean instances in a static manner.

public class Boo {

    public static void randomMethod() {
         StaticContextAccessor.getBean(Foo.class).doStuff();
    }

}

Solution 3 - Java

What you can do is @Autowired a setter method and have it set a new static field.

public class Boo {
    @Autowired
    Foo foo;
    
    static Foo staticFoo;   

    @Autowired
    public void setStaticFoo(Foo foo) {
        Boo.staticFoo = foo;
    }

    public static void randomMethod() {
         staticFoo.doStuff();
    }
}

When the bean gets processed, Spring will inject a Foo implementation instance into the instance field foo. It will then also inject the same Foo instance into the setStaticFoo() argument list, which will be used to set the static field.

This is a terrible workaround and will fail if you try to use randomMethod() before Spring has processed an instance of Boo.

Solution 4 - Java

The easiest way to create a static context is naturally, when the application starts up. This will prevent the need for an unnatural implementation with an additional class.

@SpringBootApplication
public class MyApplication {

    private static ApplicationContext appContext;


    public static void main(String[] args) {
        appContext = SpringApplication.run(MyApplication.class, args);
    }

    public static ApplicationContext getAppContext() {
        return appContext;
    }
}

Then, anywhere you need to access a bean statically, you can use the ApplicationContext to get the instance of the class.

public class Boo {
    public static void randomMethod() {
         MyApplication.getAppContext()
                            .getBean(Foo.class).doStuff();
    }
}

Regards..

Solution 5 - Java

It sucks but you can get the bean by using the ApplicationContextAware interface. Something like :

public class Boo implements ApplicationContextAware {

    private static ApplicationContext appContext;

    @Autowired
    Foo foo;

    public static void randomMethod() {
         Foo fooInstance = appContext.getBean(Foo.class);
         fooInstance.doStuff();
    }

    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        Boo.appContext = appContext;
    }
}

Solution 6 - Java

This builds upon @Pavel's answer, to solve the possibility of Spring context not being initialized when accessing from the static getBean method:

@Component
public class Spring {
  private static final Logger LOG = LoggerFactory.getLogger (Spring.class);

  private static Spring spring;

  @Autowired
  private ApplicationContext context;

  @PostConstruct
  public void registerInstance () {
    spring = this;
  }

  private Spring (ApplicationContext context) {
    this.context = context;
  }

  private static synchronized void initContext () {
    if (spring == null) {
      LOG.info ("Initializing Spring Context...");
      ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
      spring = new Spring (context);
    }
  }

  public static <T> T getBean(String name, Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(name, className);
  }

  public static <T> T getBean(Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(className);
  }

  public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
    initContext();
    return spring.context.getAutowireCapableBeanFactory ();
  }
}

The important piece here is the initContext method. It ensures that the context will always get initialized. But, do note that initContext will be a point of contention in your code as it is synchronized. If your application is heavily parallelized (for eg: the backend of a high traffic site), this might not be a good solution for you.

Solution 7 - Java

Use AppContext. Make sure you create a bean in your context file.

private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);

public static void randomMethod() {
     foo.doStuff();
}

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
QuestionTaksView Question on Stackoverflow
Solution 1 - JavaFrancisco SpaethView Answer on Stackoverflow
Solution 2 - JavaPavel HoralView Answer on Stackoverflow
Solution 3 - JavaSotirios DelimanolisView Answer on Stackoverflow
Solution 4 - JavaBludwoodView Answer on Stackoverflow
Solution 5 - JavaJean-Philippe BondView Answer on Stackoverflow
Solution 6 - JavaHashkenView Answer on Stackoverflow
Solution 7 - JavaVijayView Answer on Stackoverflow