Spring Bean Removal
In one of my current projects we had the requirement to remove Spring beans during application startup. The actual context being that batch programs shared the same application code then the web application. In such a situation, there is a need to exclude certain beans from starting up in the batch code and vice versa.
So we had the requirement for removing Spring beans during the application container startup. For the batch code it ment that we needed to remove all beans having some sort of HTTP-based scope. Spring comes with
@SessionScope (you can find more about Spring scopes here) and we also had two custom scopes for tab and view scopes.
All those scopes, being based on Spring‘s
@Scope annotation and CustomScopeConfigurer at some point or another use the Servlet API in order to store the corresponding state either in the HTTP request or the HTTP session.
It was about exactly those beans to remove them and start batch applications without them.
After some digging around, we decided to implement our requirement with a custom BeanFactoryPostProcessor. This interface allows for the modification of so-called bean registration objects. Note that it is bean registration objects and not beans. BeanFactoryPostProcessors are triggered by Spring after the bean meta-data is available and just before the actual bean instances are about to be created.
So this seemed like the exact hook in order to do our modifications.
In order to find beans implementing one of
@SessionScope or our own
@ViewScope scopes, we need to go through all the registered bean names:
Don’t get confused by the implementation of the
BeanFactoryPostProcessor here. It uses a Java 8 lamda to implement this SAM interface.
For every BeanDefinition we try to determine its current scope. Luckily, there is a
getScope() method on every bean definition that can be used for that.
Once the scope is one of our to-be-excluded scopes, we can simply call
removeBeanDefinition(String) from the BeanDefinitionRegistry interface, which we get with
ConfigurableListableBeanFactory as an argument to
postProcessBeanFactory our single method we have to implement with the
The actual work to determine whether the scope is a web scope or the standard Spring scope, is done in
This implementation is kind of an all-in variant. It will simply remove all beans that are not singleton or protoype scoped, so the implementation could also have been to check only the HTTP-related scopes and excludes only those. We decided for this variant in order to automatically support new scopes as there is a very high possibility new scopes will be based on the HTTP request or session in some way.
Let us now assume there is a class
IndexView in our code base:
RequestInformation is marked as request-scoped it needs to be instantiated on every request by the Spring DI container. This is usually done via proxies. Let‘s assume
RequestInformation is used from another component that is indeed a singleton:
Spring needs to replace the logical
RequestInformation reference on every request. This is done via AOP proxies. The proxy is injected like a singleton bean but implements the logic to fallback to the correct scope to delegate to the actual reference.
In order to find more about the HTTP scope bean proxing provided by Spring, ScopedProxyUtils is a good starting point. This is Spring’s utility class to create scoped proxies. When having a look at the method
createScopedProxy we see that Spring will create actually two beans for our
requestInformation bean. The proxy bean would get the name
requestInformation, the target bean gets the name
All of that is necessary to understand that there are actually two bean definitions in the bean registry for HTTP scoped beans.
UserService we would find a bean definition for
So for our
BeanPostFactoryProcessor to work, we need to take into account both bean definitions as removing either one of them would lead to errors during the container startup. So as a matter of fact, we end up with this implementation:
Spring provides various hooks to influence the way how the DI container actually creates beans. In this article we showed a way how to adapt Spring with a
BeanFactoryPostProcessor in order to remove certain beans when running in batch classes.