Sunday, August 26, 2012

Dependency Injection on Stateless Beans

I recently came across a situation where I needed to inject a dependency into a class that, when used, was expected to be stateless. I'm using Spring as the dependency injection container, and this class is instantiated directly from the class, and not through Spring context; thus, I could not use Spring context with basic dependency injection. Did my Google search, but did not find an answer, so I'm posting this because the solution was fairly simple and needed to be shared.

The scenario (assuming you're familiarized with JPA, Spring, and overall dependency injection) 
I have a utility class that is responsible for indexing entities using Lucene. I wanted to add this utility to a JPA listener so that it can create or update the index on the entity in a @PrePersist method. Why this way, and not through a service? Good question. The entity could be persisted directly through a service, directly using Entity Manager, or as a cascade effect when persisting an entity that contains this entity as a property. So, in order to ensure that the index is created/updated, I wanted to provide a listener that would do that prior to saving the entity.

The service class that creates/updates indexes needs to be instantiated by Spring context so that it can have all the indexing dependencies, such as the location of the indexes, so if I wanted a Listener to do this task, I needed to inject this instantiated bean by Spring context into the listener. But, JPA listeners are supposed to be stateless, Entity Manager does not call Spring context to get a reference of an instantiated listener, rather it uses the class specified in the @EntityListeners annotation and instantiates it directly. So, what to do?

Solution
If the search service, the service that does the indexing as well, can be declared as static in the Listener, surely it will be accessible when JPA executes a persist of the entity by any method.


@Component
public class EntityIndexListener
{
    static private SearchService searchService;
    ...




Declaring static is part of the solution, we still need to inject the instantiated class by Spring context to it. For this we create a set method, the same as we would as for any other dependency.


@Autowired(required = true)
@Qualifier("searchService")
public void setSearchService(SearchService searchService)
{
    this.searchService = searchService;
}

This will setup the dependency, assuming autowire is configured. If autowire is not configured, you can define the dependency explicitly. 

One last thing; to ensure that Spring does the injection at startup, declare an init method solely for the purpose of "telling" Spring that this method needs to be instantiated at startup, so that this method can be executed. The init method does not need to do anything.

@PostConstruct
public void init()
{
    logger.info("Initializing Search Service for Listener ["+ searchService +"]");
}

Hope this helps.