1 什么是拦截器
拦截器是相对于Spring中来说的,它和过滤器不一样,过滤器的范围更广一些是相对于Tomcat容器来说的。拦截器可以对用户进行拦截过滤处理。
但是并不是说拦截器只对请求进入Controller控制器之前起作用,它也分为3个部分:
- 请求进入Controller之前,通过拦截器执行代码逻辑
- Controller执行之后(只是Controller执行完毕,视图还没有开始渲染),通过拦截器执行代码逻辑
- Controller完全执行完毕(整个请求全部结束),通过拦截器执行代码逻辑
2HandlerInterceptor和WebMvcConfigurer
想要自己配置一个拦截器,就必须用到HandlerInterceptor和WebMvcConfigurer这两个接口。
2.1HandlerInterceptor
作用:自定义拦截器
如何创建:这个接口我们通常会自定义一个类。加上@Component注解,并且使它实现HandlerInterceptor接口(根据需求重写里面的三个方法)
HandlerInterceptor源码如下:
public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
- preHandle:此方法的作用是在请求进入到Controller进行拦截,有返回值。(返回true则将请求放行进入Controller控制层,false则请求结束返回错误信息)
用法:登录验证(判断用户是否登录)权限验证:判断用户是否有权访问资源(校验token)
- postHandle:该方法是在Controller控制器执行完成但是还没有返回模板进行渲染拦截。没有返回值。就是Controller----->拦截------>ModelAndView。
用法:因此我们可以将Controller层返回来的参数进行一些修改,它就包含在ModelAndView中,所以该方法多了一个ModelAndView参数。
- afterCompletion:该方法是在ModelAndView返回给前端渲染后执行。
用法:例如登录的时候,我们经常把用户信息放到ThreadLocal中,为了防止内存泄漏,就需要将其remove掉,该操作就是在这里执行的。
2.2WebMvcConfigurer
作用:添加拦截规则
如何创建:自定义一个类,实现WebMvcConfigurer并将它注入到Spring容器中。根据需求实现里面方法。
WebMvcConfigurer源码如下:
public interface WebMvcConfigurer { default void configurePathMatch(PathMatchConfigurer configurer) { } default void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } default void configureAsyncSupport(AsyncSupportConfigurer configurer) { } default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { } default void addFormatters(FormatterRegistry registry) { } default void addInterceptors(InterceptorRegistry registry) { } default void addResourceHandlers(ResourceHandlerRegistry registry) { } default void addCorsMappings(CorsRegistry registry) { } default void addViewControllers(ViewControllerRegistry registry) { } default void configureViewResolvers(ViewResolverRegistry registry) { } default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { } default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { } default void configureMessageConverters(List<HttpMessageConverter<?>> converters) { } default void extendMessageConverters(List<HttpMessageConverter<?>> converters) { } default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { } default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { } @Nullable default Validator getValidator() { return null; } @Nullable default MessageCodesResolver getMessageCodesResolver() { return null; } }
根据源码发现,这里面有很多方法,但是通常我们只用到了里面的两种方法如下:
addInterceptors:添加拦截器,拦截器需要拦截的路径和需要排除拦截的路径都需要在其中配置
addResourceHandlers:配置静态资源路径,即某些请求需要读取某个路径下的静态资源内容,需要配置该静态资源的路径,通过该方法可以统一给这些请求配置指定静态资源路径
3 拦截器实现流程
根据第二部分,我们知道了配置拦截器的准备工作。
1)自定义类实现HandlerInterceptor
@Component public class HeaderInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入拦截器=======执行前========"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("进入拦截器=======执行中========"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("进入拦截器=======执行后========"); } }
2)自定义类实现WebMvcConfigurer
package com.liubujun.springbootinterceptor.config; import com.liubujun.springbootinterceptor.interceptor.HeaderInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @Author: liubujun * @Date: 2022/10/10 19:55 */ /** * 此处也可以继承WebMvcConfigurationSupport */ @Configuration public class WebAppConfig implements WebMvcConfigurer { @Resource private HeaderInterceptor headerInterceptor; /** * 添加拦截规则 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { List<String> patterns = new ArrayList<>(); patterns.add("/login/login"); registry.addInterceptor(headerInterceptor) .addPathPatterns("/**") //所有的请求都要拦截。 .excludePathPatterns(patterns); //将不需要拦截的接口请求排除在外 } /** * //下面代码意思是:配置一个拦截器,如果访问路径时addResourceHandler中的这个路径(我这里是/**表示所有的路径), * 那么就映射到访问本地的addResourceLocations这个路径上,这样就可以看到该路径上的资源了 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") //配置需要添加静态资源请求的url .addResourceLocations("classpath:/pic/"); //配置静态资源路径 } }
3)测试类
@RestController public class LoginController { @GetMapping("/login/login") public String testLoginInterceptor(){ return "登录请求没有被拦截====》看控制台没有输出"; } @GetMapping("/test/other") public String testOtherInterceptor(){ return "该请求没有被拦截,看控制台有输出"; } }
4)本地添加静态资源
猜验证1:
由于在配置拦截规则中,拦截了所有请求,但是排除了登录请求,所以
/login/login 请求正常访问,但不会进入拦截器,故控制台不会打印拦截器中的输出语句。
验证2:
由于在配置拦截规则
由于在配置拦截规则中,拦截了所有请求,但是排除了登录请求,所以
/test/other请求正常访问,会进入拦截器,控制台会打印拦截器中的输出语句。