Using Servlet Filter and Spring Web with Jetty
I've been experimenting with servlet filter, along the same lines as my experimentation with aspects. Aspects are quite an interesting topic, however for the request instrumention task it seems that filters are much more suitable. Realising your approach is wrong and pivoting is, I feel, an important skill to learn in software development.
I must point out something up-front. In writing the example for this article, I came to the realisation that spring boot takes care of a lot. I decided to write these examples without using spring boot, in hope that I would learn more about how servlets and containers work — I wasn't disappointed. It handles all the complexities of servlet initialisation, filter registration, embedded servlet containers and so much more.
Registering Servlet Filters with Spring #
With Spring Boot, you may define an implementation of Filter
as a @Bean
. A Spring Boot application will—by default—register this as a servlet filter. Alternately, you can define a FilterRegistrationBean
which allows you to modify the filter configuration, such as path matching and execution ordering. If you do both, spring seems to automatically register the FilterRegistrationBean
over the Filter
, giving you a great deal of flexibility.
@Bean
public FilterRegistrationBean getExampleFilter(ExampleServletFilter exampleServletFilter){
FilterRegistrationBean registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(exampleServletFilter);
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
With Spring Web, you must register a filter before a ContextLoaderListener
initializes the context. The servlet context must become read-only after being initialised, except as dictated by the Servlet 3 specification section 4.4. WebApplicationInitializer::onStartup
is typically used to initialize both the application and servlet contexts, so filters must be registered during this process. Normally, this would stop you from registering bean instances as filters from the spring IoC container. Spring provides DelegatingFilterProxy
to allow spring-managed beans be used as filters, as demonstrated below;
DelegatingFilterProxy exampleServletFilter = new DelegatingFilterProxy("exampleServletFilter");
servletContext.addFilter("exampleServletFilter", exampleServletFilter)
.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST),
true,
"/example-filtered/*",
"/example-exception/*"
);
You must refer to your bean by a defined name, rather than it's Class<>
. The initialization is the main difference. Your application code must set up any Filter
up front, whereas with Spring Boot this can be wired up in configuration or external dependencies.
A note on Servlet Filter Ordering #
Spring Boot's FilterRegistrationBean
and @Bean
support explicit ordering. This is demonstrated above, using FilterRegistrationBean::setOrder
, or alternately the @Order
annotation. Servlet filters don't support ordering when using Java-based servlet configuration, which means that there's no explicit way to order filters in our example.