I still remember the first time I used Spring Framework's dependency injection - it seemed almost magical in how it simplified (backend) web application development, and allowed me to do things with simple annotations that would have taken much effort and design to get right.
@Autowired's magic however can only be used on Spring managed beans (instantiated objects that are managed by Spring). However, there are many situations where you might need access to a Spring bean from non-Spring manged classes and POJO's (Plain Old Java Objects).
So, the question we're going to provide a simple solution for is:
How can I inject Spring managed beans into POJO's and classes not managed by Spring?
Researching this question turned up several solutions including approaches involving AspectJ weaving and some other solutions which all required quite a bit of setup, including adding various jars and other maven dependencies. I tried most of these and couldn't get a simple and workable solution that required introducing no special jar or maven dependencies, and could be used with any Spring app.
I happened across a very cool article on the coDippa blog. It covered an approach where you essentially create a static reference to a special Spring bean (
ApplicationContext) from which you can then access any other Spring managed bean. Since it's a static method, any POJO or non-Spring managed bean can access said instance of
ApplicationContext and use that to get the desired Spring bean(s).
Below I've largely just simplified coDippa's approach (and provided simpler access methods) so all props go to him.
Let's use an actual use case to demonstrate
So, I wanted to create a custom logger (I chose to extend on Apache's Log4j) that not only logged certain operations on my web application, but also logged which authenticated user was initiating said operation(s). A good example here was in an authenticated RESTful service API I had implemented - I wanted to log which user had sent which queries to my REST endpoint.
To be able to get that user information, I needed to access my Spring managed service class,
UserService (which included methods to get current logged in users using Spring's
SecurityContext bean). However, many of the classes that contained methods and operations I wanted to log were not Spring managed at all (including several utility method classes). Hence I couldn't just inject with
UserService instance into everything. Enough talk, some code...
My simple logger which contains the method
infoUser(Object) which prepends the logger info message with the user's name:
You'll notice that my
AppLogger class is a POJO that extends
Log4j. You'll also notice that it depends on getting the currently logged in user with the method
Finally, you'll see that I get the Spring managed instance of
UserService with the method
This is where the magic happens. It requires only a very simple class which implements the Spring interface
That's it... all you need to provide access of Spring managed beans to POJO classes.
So what's going on here? Well you should note that
SpringContext IS a Spring managed class so it should be scanned by your
@Component scans). During application initialisation the method
setApplicationContext will be called with a Spring bean instance of
ApplicationContext passed to it. The important thing is this instance provides access to any other Spring bean. We then expose a simple static
getBean(Class<T> ...) method which returns the Spring managed bean instance of a service class (in this case) that any POJO class can access.
In the example above, my AppLogger.
getUserService() method simply returns the fetched Spring bean instance of
UserService class with this static method. Note that you can call any Spring managed bean here by simply passing the getBean(...) method the class for said bean. E.g.
So there you have it - one simple class that can provide any POJO access to specific Spring beans without crazy wiring or needing other specialised libraries.