Explain Codes LogoExplain Codes Logo

@autowired and static method

java
dependency-injection
spring-framework
best-practices
Anton ShumikhinbyAnton Shumikhin·Jan 30, 2025
TLDR
@Component public class ServiceHolder { private static MyService staticService; @Autowired public void init(MyService myService) { ServiceHolder.staticService = myService; } public static void useStaticService() { //It's not magic, it's a hack! staticService.performAction(); } }

Now useStaticService() can utilize the static staticService which is updated once Spring initializes the bean.

Deciphering the static field and @Autowired

Issue with static fields

Static fields belong to the class, not instances. This presents a hurdle for Spring's DI (Dependency Injection) via @Autowired, as it's intended for non-static fields methods. When you tag a non-static field with @Autowired, Spring automatically injects the right bean.

In the case of static fields, you can't directly employ @Autowired since these fields aren't tied to any instance and Spring functions around instance lifecycle and bean dependencies.

Employing @PostConstruct for setting static fields

You can bypass this limitation using the @PostConstruct annotation:

@Component public class BeanHolder { private static SomeBean someBean; private SomeBean nonStaticSomeBean; @Autowired public BeanHolder(SomeBean nonStaticSomeBean) { this.nonStaticSomeBean = nonStaticSomeBean; } @PostConstruct private void initStatic() { // Hey there static, meet non-static. You guys play nice now! BeanHolder.someBean = nonStaticSomeBean; } }

This tells Spring to run the initStatic method after the bean is fully initialized. This way, you assign the just constructed bean to the static field, making it accessible to any static method.

Incorporating ApplicationContextAware interface

By implementing ApplicationContextAware, you can store the ApplicationContext in a static variable:

@Component public class ApplicationContextProvider implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) { ApplicationContextProvider.context = applicationContext; } public static <T> T getBean(Class<T> beanClass) { return context.getBean(beanClass); } }

With this setup, you can access beans in static methods via getBean() method, acting like a responsible adult around bean lifecycle.

Pivoting towards best practices for static access

Avoid static unless necessary

First, reconsider whether you essay a static method or if it’s a design flaw. Dependency injection is designed for instance-oriented scenarios which offer easier testing and mocking.

Static setters with lifecycle awareness

If you must use a static field, inject them using a static setter. This enables a Spring managed instance to populate the static field during creation:

public class StaticFieldInjector { private static SomeBean staticBean; @Autowired public void setStaticBean(SomeBean bean) { // I've got a bean and I'm not afraid to use it! StaticFieldInjector.staticBean = bean; } }

Heed caution with static setters; they may fail to perform if accessed before Spring finishes initialization. Nobody likes a half-baked cake, right?

StaticContextAccessor as a fallback

As a last resort, create a StaticContextAccessor with @Component to obtain beans statically. This approach allows you to retain Spring's dependency management comforts, while offering static bean access:

@Component public class StaticContextAccessor { private static ApplicationContext applicationContext; @Autowired public StaticContextAccessor(ApplicationContext context) { // Like putting all your beans in one silo and hoping it doesn't explode StaticContextAccessor.applicationContext = context; } public static <T> T getBean(Class<T> beanClass) { // It's like Amazon for your beans! One-stop shop return applicationContext.getBean(beanClass); } }

This should be considered a last resort as it goes against principles of encapsulation and dependency inversion.