Spring Boot 拦截器的配置和使用

目录

    • 拦截器的作用
    • 拦截器的设计
    • HandlerInterceptor源码
    • 拦截器执行流程
      • 拦截器执行流程图
      • 拦截器执行步骤
    • 在项目中如何使用拦截器?
      • 自定义拦截器
      • 注册拦截器
        • 继承`WebMvcConfigurationSupport`类后静态资源配置失效的原因
        • 拦截器的匹配规则
        • 静态资源放行规则
      • 多个拦截器的执行顺序

拦截器的作用

当请求来到 DispatcherServlet 时, 它会根据 HandlerMapping 的机制找到处理器, 这样就会返回一个 HandlerExecutionChain 对象,这个对象包含处理器和拦截器。这里的拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。

拦截器的设计

首先所有的拦截器都需要实现 HandlerInterceptor 接口,该接口源码定义如下

HandlerInterceptor源码

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;

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 {
	}
}

注意:上述源码是Spring Boot2.x,该版本要求JDK1.8+。也正因为这个接口是 Java 8 的接口,所以 3 个方法都被声明为 default, 并且提供了空实现。当我们需要自己定义方法的时候, 只需要实现 HandlerInterceptor, 覆盖其对应的方法即可。

拦截器执行流程

拦截器执行流程图

Spring Boot 拦截器的配置和使用_第1张图片

拦截器执行步骤

  1. 执行 preHandle 方法,该方法会返回一个布尔值。如果为 false,则结束所有流程:如果为 true, 则执行下一步
  2. 执行处理器逻辑,它包含控制器的功能
  3. 执行 postHandle方法
  4. 执行视图解析和视图渲染
  5. 执行afterCompletion 方法

在项目中如何使用拦截器?

学习了拦截器的执行流程之后,就要开始学习拦截器的开发规范了,其实要在项目中使用它,也是非常的简单,开发者只需要两步就可以在项目中灵活的使用了。

  1. 实现HandlerInterceptor接口开发自己的拦截器
  2. 将拦截器注册到web容器中

自定义拦截器

实现 Handlerlnterceptor 接口,然后按照自己的需要重写拦截器方法。

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        log.info("请求路径uri:{}", request.getRequestURI());
        String token = request.getHeader(Constant.TOKEN);
        if (StringUtils.isNotBlank(token) && Objects.isNull(request.getSession().getAttribute(token))) {
            return false;
        }
        return true;
    }

}

有了拦截器, Spring MVC 并不会发现它,它还需要进行注册才能够拦截处理器。

注册拦截器

拦截器的注册有两种方式,无论是哪种方式,都是覆盖其 addInterceptors方法。

  1. 实现WebMvcConfigurer接口
  2. 继承WebMvcConfigurationSupport
    继承WebMvcConfigurationSupport后会导致自动配置【templates和static】失效,所以用了这种方式后有需要的话【项目中还有前端页面和资源的前提下】还要指定页面和静态资源的位置。即需要重写addResourceHandlers方法,对于现在前后端分离的项目,这就是无所谓的事情。

继承WebMvcConfigurationSupport类后静态资源配置失效的原因

这部分的学习需要具备一定的Spring Boot源码分析能力,没学习过的朋友可以先参考楼主这篇文章了解一下:Spring Boot源码分析
分析如下:
spring-boot-autoconfigure-2.1.10.RELEASE.jar的spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration=\里配置有WebMvcAutoConfiguration
Spring Boot 拦截器的配置和使用_第2张图片
该类定义如下

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

划重点@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

  • @ConditionalOnMissingBean
    仅在该注解规定的类不存在于 spring容器中时,使用该注解的config或者bean声明才会被实例化到容器中

在这里因为“继承”的特性,该项目类路径中缺少WebMvcConfigurationSupport类型的bean时该自动配置类才会生效,当我们继承了 WebMvcConfigurationSupport 时,该类就不会被扫描到了,因此默认的配置就生效了!

拦截器的匹配规则

无论使用上述哪种规则,拦截器的配置都是在addInterceptors方法内完成,具体规则如下

  • addPathPatterns:指定拦截规则,正则匹配,只会拦截匹配的请求
  • excludePathPatterns:表示排除的请求
@Override
protected void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/**")
            .excludePathPatterns("/login");
    super.addInterceptors(registry);
}

静态资源放行规则

对于某些静态资源我们并不希望它被拦截,此时可以重写addResourceHandlers方法,当然前后端分离的项目【前端一个项目,后端一个项目】,就不需要配置了。

  • addResourceHandler:静态资源访问的URL
  • addResourceLocations:静态资源所在路径
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/swagger-ui.html")
            .addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
    registry.addResourceHandler("/webjars/**")
            // 注意如下的映射资源路径,不能写/**
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
    super.addResourceHandlers(registry);
}

多个拦截器的执行顺序

多个拦截器的执行结果是责任链模式的规则,对于处理器前方法采用先注册先执行,而处理器后方法和完成方法则是先注册后执行的规则。是不是和AOP很相似,楼主对这种规则有一个更简单直观的理解【脑补一个同心圆】。
当拦截器链内存在多个拦截器时,处理器前(preHandle)方法会执行,但是一旦有一个返回 false,则后续的拦截器、 处理器和所有拦截器的处理器后(postHandle) 方法都不会被执行。而完成方法 afterCompletion 则不一样,它只会执行返回 true 的拦截器的完成方法,而且顺序是先注册后执行。

最后,没有案例验证的博文扯再多都是耍流氓,案例呈上:Spring Boot【customize-auth子项目】

你可能感兴趣的:(SpringBoot,自定义拦截器,拦截器源码分析)