Dynamic bean definition for automatic FilterRegistrationBeanFactory unregistration
Sometimes you need to define beans dynamically (e.g. you need to generate many beans because the bean definitions depend on external conditions) since you cannot declare them via xml/java/annotation config. For these situations, it is possible to generate bean definitions dynamically.
Dynamic bean creation
Typically, the bean creation workflow happens as follows:
To declare new beans, there are 2 places to add custom logic:
- (1)
BeanFactoryPostProcessor
: we can modify theBeanFactory
so it knows about new beans before the context initialization phase. - (2) After the
ApplicationContext
creation phase, we can reconfigure theBeanFactory
, keeping in mind that these beans won’t be autowired to other beans.
In both approaches, we need to create a BeanDefinition
. The preferred implementation of the BeanDefinition
interface is GenericBeanDefinition
. Additionally, there is a useful class called BeanDefinitionBuilder
that can help with bean definition creation. Let’s try to use it!
Here’s a simple example of the 2nd approach:
If you launch the above example, you’ll see that initially we don’t have testBean
inside the context, but then it becomes available.
Let’s now look at a more complex example that demonstrates the 1st approach:
Above we defined a bean named beanFactoryPostProcessor
that is responsible for modifying the BeanFactory
and added a bean definition with the following specification:
- It’s a bean of the class
TestBean
. By default, it’s a singleton bean instantiated using its constructor. - The required field
value
will be passed to the constructor by Spring during the bean creation and specified by the methodaddConstructorArgReference
.
If you launch this example, it will work as expected and the console will print the following:
Test bean with value: My test String Bean
In practice: FilterRegistrationBeanFactory unregistration
Now we can talk about a few real world examples. I’ve been using Spring very actively for about 3 years but I used this technique only once.
Here’s a spring boot app:
Yes, it is only one class. If you launch it, you will see this:
2015-11-27 20:10:03.066 INFO 4527 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] 2015-11-27 20:10:03.067 INFO 4527 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2015-11-27 20:10:03.067 INFO 4527 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
It says that spring registered some filters by itself and mapped each one to /*
. I didn’t ask it to do that. What if I don’t need them? In my specific case, I’m configuring specific rules by filter chain, so I need to disable all default filter mappings. I found out that other people were having the same problem (spring boot issue tracker and
stackoverflow thread).
The aforementioned solutions suggest the following solution:
What if I have a large number of filters? This approach becomes unwieldy. Let’s generate multiple FilterRegistrationBean
s for all filters dynamically. Here’s my BeanFactoryPostProcessor
:
As you can see, the BeanDefinition reflects a suggested bean definition and now all the default filters are disabled.
2015-11-27 21:09:24.745 INFO 5119 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*] 2015-11-27 21:09:24.746 INFO 5119 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Filter orderedHiddenHttpMethodFilter was not registered (disabled) 2015-11-27 21:09:24.746 INFO 5119 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Filter filterChainProxy was not registered (disabled) 2015-11-27 21:09:24.746 INFO 5119 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Filter orderedCharacterEncodingFilter was not registered (disabled)
All code examples for this post can be found at my github profile.
UPDATE: Right after I posted this blog, I learned that there’s an interface called BeanDefinitionRegistryPostProcessor
that is meant exactly for this type of BeanFactory modifications since Spring 3.0.1. It seems it’s a preferable interface to use and the code snippet described in this post can be used as-is with this interface.