1. Interceptors and filters
Before talking about Spring boot, let's first look at filters and interceptors. The two are very similar in terms of functions, but there is still a big gap in specific technical implementation. Before analyzing the differences between the two, let us first understand the concept of AOP. AOP is not a specific technology, but a programming idea. In the process of object-oriented programming, it is easy for us to solve vertical expansion through inheritance and polymorphism. However, for horizontal functions, such as enabling transactions in all service methods, or unified logging, object-oriented functions cannot be solved. Therefore, AOP-oriented programming is actually a supplement to the idea of object-oriented programming. The filters and interceptors we are talking about today are both specific implementations of aspect-oriented programming. The main differences between the two include the following aspects:
1. Filter depends on Servlet containers and is part of the Servlet specification. Interceptors exist independently and can be used under any circumstances.
2. The execution of Filter is completed by the Servlet container callback, and the interceptor is usually executed through a dynamic proxy.
3. The life cycle of the Filter is managed by the Servlet container, while the interceptor can be managed through IoC containers. Therefore, instances of other beans can be obtained through injection and other methods, so it will be more convenient to use.
2. Filter configuration
Now we use filters to realize the function of recording the execution time of requests, which is implemented as follows:
public class LogCostFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { long start = System.currentTimeMillis(); filterChain.doFilter(servletRequest,servletResponse); System.out.println("Execute cost="+(System.currentTimeMillis()-start)); } @Override public void destroy() { }}The logic of this code is relatively simple, which is to record the timestamp before the method is executed, and then complete the execution of the request through the filter chain, and calculate the execution time between the returned results. The main thing here is that this class must inherit the Filter class. This is the specification of Servlet, which is no different from previous web projects. However, with the filter class, the previous web projects can be configured in web.xml, but the spring boot project does not have the web.xml file, so how to configure it? In Spring boot, we need FilterRegistrationBean to complete the configuration. The implementation process is as follows:
@Configurationpublic class FilterConfig { @Bean public FilterRegistrationBean registrationFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new LogCostFilter()); registration.addUrlPatterns("/*"); registration.setName("LogCostFilter"); registration.setOrder(1); return registration; }}This configuration is completed. The options that need to be configured mainly include instantiating the Filter class, then specifying the matching pattern of the url, setting the filter name and execution order. This process is actually no different from configuration in web.xml, but the form is just different. Now we can start the server to access any URL:
You can see that the above configuration has taken effect. In addition to configuring through FilterRegistrationBean, there is also a more direct way, which can be completed directly through annotations:
@WebFilter(urlPatterns = "/*", filterName = "logFilter2")public class LogCostFilter2 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { long start = System.currentTimeMillis(); filterChain.doFilter(servletRequest, servletResponse); System.out.println("LogFilter2 Execute cost=" + (System.currentTimeMillis() - start)); } @Override public void destroy() { }}Here you can configure it directly with @WebFilter. Similarly, you can set the url matching mode, filter name, etc. One thing to note here is that the annotation @WebFilter is a specification of Servlet 3.0 and is not provided by Spring boot. In addition to this annotation, we also need to add another annotation to the configuration class: @ServletComponetScan, specifying the scanned package.
@SpringBootApplication@MapperScan("com.pandy.blog.dao")@ServletComponentScan("com.pandy.blog.filters")public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); }} Now, let's visit any URL again:
As you can see, both filters we configured have taken effect. Careful readers will find that we do not specify the order of execution of the second Filter, but execute before the first Filter. It should be explained here that the @WebFilter annotation does not specify the execution order attribute. Its execution order depends on the name of the Filter and is arranged in reverse order based on the alphabetical order of the Filter class name (note that it is not the name of the configured filter). The filter priority specified by @WebFilter is higher than the filter configured by the FilterRegistrationBean. Friends who are interested can experiment on their own.
3. Interceptor configuration
We have already introduced the filter configuration method above. Next, let’s take a look at how to configure an interceptor. We use an interceptor to implement the same function above, recording the execution time of the request. First we implement the interceptor class:
public class LogCostInterceptor implements HandlerInterceptor { long start = System.currentTimeMillis(); @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { start = System.currentTimeMillis(); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("Interceptor cost="+(System.currentTimeMillis()-start)); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { }}Here we need to implement the HandlerInterceptor interface. This interface includes three methods. The preHandle is executed before the request is executed, and the postHandler is executed after the request is executed, but it will only be executed when the preHandle method returns true. AfterCompletion is executed after the view rendering is completed. PreHandle also needs to return true. This method is usually used for cleaning resources and other tasks. In addition to implementing the above interface, we also need to configure it:
@Configurationpublic class InterceptorConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogCostInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); }}Here we inherited WebMVCConfigurerAdapter. Friends who have read the previous articles should have seen this class. We have used this class when configuring the static resource directory. Here we have rewritten the addInterceptors method to configure the interceptor. There are two main configuration items: one is to specify the interceptor and the other is to specify the interceptor URL. Now we start the system and access any URL:
As you can see, we achieved the same function through the interceptor. However, it should be noted here that this implementation is actually problematic, because preHandle and postHandle are two methods, so we have to set a shared variable start to store the start value, but this will have thread safety problems. Of course, we can solve this problem through other methods, such as ThreadLocal, which can be solved well, and students who are interested can implement it themselves. However, through this we can actually see that although interceptors are better than filters in many scenarios, in this scenario, filters are simpler to implement than interceptors.
4. Summary
This article mainly explains the configuration of filters and interceptors based on Spring boot. Both filters and interceptors belong to the specific implementation of AOP (sectional-oriented programming) idea. In addition to these two implementations, we have also seen another more flexible AOP implementation technology, namely Aspect, where we can complete more and more powerful functions through Aspect. I'll share this with you later.