Spring boot: configuration of multiple interceptors
There are two ways to implement interceptors in spring boot: the HandlerInterceptor interface and the filter interceptor using servlet
1, Implement the HandlerInterceptor interface
1. Create two classes to implement the HandlerInterceptor interface
The custom interceptor HandlerInterceptor implements three methods
- preHandle: before calling a method of Controller
- After postHandle:Controller is invoked, if the controller Controller has an exception before the rendering of the view, it will not execute this method.
- afterCompletion: this afterCompletion will be called for resource cleanup whether there is an exception or not
OneInterceptor.java
package com.mye.hl18springbootinterceptor.intercept; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Interceptor I */ @Component public class OneInterceptor implements HandlerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(OneInterceptor.class.getName()); /** * Preprocessing callback method to realize processor preprocessing * Return value: true indicates to continue the process; false indicates that the process is interrupted and will not continue to call other interceptors or processors */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { String url = String.valueOf(request.getRequestURL()); LOGGER.info("1,url==" + url); // Let go of the interception return true; } /** * The post-processing callback method implements the post-processing of the processor (controller), but before rendering the view * At this time, we can process model data or view through modelAndView */ @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { LOGGER.info("1,postHandle"); } /** * Callback method after the whole request is processed, that is, callback when the view rendering is completed, * For example, in performance monitoring, we can record the end time and output the consumption time here, * You can also clean up some resources, similar to finally in try catch finally, * But only call the processor in the execution chain */ @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { LOGGER.info("1,afterCompletion"); } }
TwoInterceptor.java
package com.mye.hl18springbootinterceptor.intercept; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Interceptor II */ @Component public class TwoInterceptor implements HandlerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(TwoInterceptor.class.getName()); /** * Preprocessing callback method to realize processor preprocessing * Return value: true indicates to continue the process; false indicates that the process is interrupted and will not continue to call other interceptors or processors */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { String url = String.valueOf(request.getRequestURL()); LOGGER.info("2,url==" + url); // Let go of the interception return true; } /** * The post-processing callback method implements the post-processing of the processor (controller), but before rendering the view * At this time, we can process model data or view through modelAndView */ @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { LOGGER.info("2,postHandle"); } /** * Callback method after the whole request is processed, that is, callback when the view rendering is completed, * For example, in performance monitoring, we can record the end time and output the consumption time here, * You can also clean up some resources, similar to finally in try catch finally, * But only call the processor in the execution chain */ @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { LOGGER.info("2,afterCompletion"); } }
2. Inject interceptors into Web configuration files
Spring Boot1.0 can be completed by inheriting WebMvcConfigurerAdapter, but this adapter class is discarded in Spring Boot2.0, so we can directly implement WebMvcConfigurer to complete the addition of interceptors
After implementing WebMvcConfigurer, you can select the method you want to override. Here, override addInterceptors to add a custom interceptor.
-
addInterceptor is used to add your custom interceptor instance
-
addPathPatterns is used to add URLs to be intercepted. You can write multiple URLs.
-
excludePathPatterns is used to add URLs that do not need to be intercepted. You can write multiple URLs.
package com.mye.hl18springbootinterceptor.config; import com.mye.hl18springbootinterceptor.intercept.OneInterceptor; import com.mye.hl18springbootinterceptor.intercept.TwoInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * Web configuration file */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private OneInterceptor oneInterceptor; @Autowired private TwoInterceptor twoInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // Intercept all paths and multiple interceptors form an interceptor chain // Register two custom interceptors // addPathPatterns is used to add interception rules, / * * means to intercept all requests, / * / means to intercept directories // excludePatterns user exclusion blocking //The interception order of interceptors is executed in the order of injecting interceptors in the Web configuration file registry.addInterceptor(oneInterceptor).addPathPatterns("/**"); registry.addInterceptor(twoInterceptor).addPathPatterns("/**"); // registry.addInterceptor(oneInterceptor).addPathPatterns("/**") // .excludePathPatterns("/stuInfo/getAllStuInfoA","/account/register"); } }
3. controller control layer
@RestController public class InterceptController { @RequestMapping("/reqUrl") public String reqUrl() { return "success"; } }
4. Test results
Normal interceptor execution sequence
Summary:
Only when interceptor 1 is released can interceptor 2 preHandle be executed.
Interceptor 2 preHandle will not be released, and interceptor 2 postHandle and afterCompletion will not be executed.
As long as an interceptor is not released, the postHandle will not execute.
5. Solve problems
excludePathPatterns failure
This phenomenon is that you add a path to be ignored in the excludePathPatterns method, but when you access this path, the interceptor still intercepts it.
This is because the path you want to ignore does not exist in the project. springboot will program the path / error, so it cannot be excluded
The problem of static resources being intercepted
In spring boot 1.0, our custom interceptor does not intercept static resources, but in spring boot 2.0, our custom interceptor also intercepts static resources
resolvent:
Because the custom interceptor intercepts all paths, first we need to override addResourceHandlers() method to specify the access path prefix of static resources and the path of static resources:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //The first method sets the access path prefix, and the second method sets the resource path registry.addResourceHandler("/resources/**","/public/**") .addResourceLocations("classpath:/resources/","classpath:/public/"); }
Then ignore the path prefix of static resources when adding custom interceptors:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/","user/login","/index.html","/error.html") .excludePathPatterns("/public/**","/resources/**"); }
Finally, when accessing static resources, add the full path of the resource, such as
Spring boot1.0 can access static resources in this way:
localhost:8080/11.png
Then Spring Boot2.0 plus a custom interceptor must be like this:
localhost:8080/public/11.png,localhost:8080/resources/11.png
2, filter interceptor using servlet
import java.io.IOException; import java.util.Arrays; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; @Component @WebFilter(urlPatterns="/**",filterName="loginFilter") public class LoginFilter implements Filter{ //Exclude URLs that are not blocked private static final String[] excludePathPatterns = { "/stuInfo/getAllStuInfoA"}; @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; // Obtain the request url address, and do not intercept the url in excludePathPatterns String url = req.getRequestURI(); if (Arrays.asList(excludePathPatterns).contains(url)) { //Release, which is equivalent to the return value of LoginInterceptor in the first method is true chain.doFilter(request, response); } System.out.println("Start intercepting................"); //Business code } @Override public void destroy() { // TODO Auto-generated method stub } }
The difference between the two ways
Filter and Interceptor Both AOP The embodiment of programming ideas and functions can be basically realized The interceptor is more powerful, Filter It can do whatever it can Filter In only in Servlet Back and forth, and Interceptor It can go deep into before and after the method, before and after the exception throw, etc Depend on Servlet Container both web Application, and Interceptor Not dependent on Servlet Containers can therefore run in a variety of environments In the life cycle of interface calls, Interceptor Can be called multiple times, and Filter Can only be called once when the container is initialized. Filter and Interceptor Execution sequence of Before filtration->Before interception->action implement->After interception->After filtration