How do you get a Spring Bean without Dependency Injection?
Sometimes, you want to use a Spring Bean in a class that is not a Spring Bean, but then dependency injection doesn’t work. This article shows a way to get around that limitation. This article delves into a solution to circumvent this limitation by introducing a method to access Spring Beans without relying on dependency injection.
Introducing ApplicationContextHolder
The Spring Framework provides an interface called
. From the JavaDoc: “Interface to be implemented by any object that wishes to be notified of the ApplicationContextAware
ApplicationContext
that it runs in.”. Sound precisely what we need. So, let’s create an implementation of this interface:
@Component public class ApplicationContextHolder implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } }
Fine, but what do we do with the ApplicationContext
passed to the setApplicationContext
method? We could store it in a static variable, and the added methods get Spring Beans! Let’s have a look at the final implementation:
@Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; public static <T> T getBean(Class<T> type) { return applicationContext.getBean(type); } public static <T> T getBean(String name, Class<T> type) { return applicationContext.getBean(name, type); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextHolder.applicationContext = applicationContext; } }
Excellent, now we have two static methods to access Spring Beans by type or by name! Let’s see how we can use it. For example, we want to access the applicationTaskExecutor by name to run a task asynchronously:
var taskExecutor = getBean("applicationTaskExecutor", TaskExecutor.class); taskExecutor.execute(() -> { ... });
Or we want to get a Bean by type:
var translationProvider = ApplicationContextHolder.getBean(TranslationProvider.class); translationProvider.getTranslation("Offen", getLocale());
Cautions and Best Practices
After publishing the article Réda Housni Alaoui sent me a message on Twitter explaining that this approach can lead to problems in situations where you have multiple ApplicationContexts
, for example when you run tests. Spring Boot Test tries to cache the Application Context, but when you have different configurations or in certain other situations you will have more than one. I created a test project, and Réda helped me to reproduce the issue. You can see the test failing in the branch “couple-a-and-b”.
To overcome this problem, you can set the ApplicationContext in the ApplicationContextHolder
before the test runs. In the main branch, you’ll find the AbstractBaseTest
that sets the ApplicationContext
in the BeforeEach
method. This way, we make sure that the ApplicationContextHolder
uses the ApplicationContext
that is active during the test.
public abstract class AbstractBaseTest { @Autowired protected ApplicationContext applicationContext; @Autowired private ApplicationContextHolder applicationContextHolder; @BeforeEach void setApplicationContext() { applicationContextHolder.setApplicationContext(applicationContext); } }
Conclusion and Use Cases
As you can see, it’s straightforward to create a helper class that holds the ApplicationContext and provides methods to get Beans. But you have to be careful when using it in tests where you could have multiple AppllicationContexts.
If you develop UIs, for example, with Vaadin, this is very handy because you don’t want to make every UI component a Spring Bean, and on the other side, you don’t want to pass references to Spring Beans to all UI components.
Get Expert Guidance on Spring Bean Access
Need help implementing Spring Bean access strategies for your unique project? Contact me today.
Related Posts
- Durable Subscription with JMS and Spring Boot
- Testing Spring Boot JMS with ActiveMQ Artemis and Testcontainers
- Build Secure Web Apps with Vaadin & Spring Boot