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:
BeanFactoryPostProcessor: we can modify the
BeanFactoryso it knows about new beans before the context initialization phase.
- (2) After the
ApplicationContextcreation phase, we can reconfigure the
BeanFactory, 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
valuewill be passed to the constructor by Spring during the bean creation and specified by the method
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
The aforementioned solutions suggest the following solution:
What if I have a large number of filters? This approach becomes unwieldy. Let’s generate multiple
FilterRegistrationBeans for all filters dynamically. Here’s my
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.