Spring MVC 源码 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

news2025/2/24 12:02:13

HandlerMapping 组件

HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors

  • handler 处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法

  • HandlerInterceptor 拦截器对处理请求进行增强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理

HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

在上一篇《HandlerMapping 组件(一)之 AbstractHandlerMapping》文档中分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类,在获取HandlerExecutionChain 处理器执行链时,会去寻找匹配的 HandlerInterceptor 拦截器们,并添加到其中。那么本文将分享 Spring MVC 的拦截器相关内容

HandlerInterceptor

org.springframework.web.servlet.HandlerInterceptor,处理器拦截器接口,代码如下:

public interface HandlerInterceptor {
    /**
     * 前置处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行之前
     */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    /**
     * 后置处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后
     */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 完成处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行之后(无论成功还是失败)
     * 条件:执行 {@link #preHandle(HttpServletRequest, HttpServletResponse, Object)} 成功的拦截器才会执行该方法
     */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }
}

HandlerExecutionChain

org.springframework.web.servlet.HandlerExecutionChain,处理器执行链,也就是通过 HandlerMapping 组件为请求找到的处理对象,包含处理器(handler)和拦截器们(interceptors

构造方法

public class HandlerExecutionChain {
    /**
     * 处理器
     */
    private final Object handler;

    /**
     * 拦截器数组
     */
    @Nullable
    private HandlerInterceptor[] interceptors;

    /**
     * 拦截器数组。
     *
     * 在实际使用时,会调用 {@link #getInterceptors()} 方法,初始化到 {@link #interceptors} 中
     */
    @Nullable
    private List<HandlerInterceptor> interceptorList;

    /**
     * 已成功执行 {@link HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)} 的位置
     *
     * 在 {@link #applyPostHandle} 和 {@link #triggerAfterCompletion} 方法中需要用到,用于倒序执行拦截器的方法
     */
    private int interceptorIndex = -1;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[]) null);
    }

    public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList<>();
            // 将原始的 HandlerExecutionChain 的 interceptors 复制到 this.interceptorList 中
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            // 将入参的 interceptors 合并到 this.interceptorList 中
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        } else {
            this.handler = handler;
            this.interceptors = interceptors;
        }
    }
}
  • handler:请求对应的处理器对象,可以先理解为 HandlerMethod 对象(例如我们常用的 @RequestMapping 注解对应的方法会解析成该对象),也就是我们的某个 Method 的所有信息,可以被执行

  • interceptors:请求匹配的拦截器数组

  • interceptorList:请求匹配的拦截器集合

  • interceptorIndex:记录已成功执行前置处理的拦截器位置,因为已完成处理只会执行前置处理成功的拦截器,且倒序执行

addInterceptor

addInterceptor(HandlerInterceptor interceptor) 方法,添加拦截器到 interceptorList 集合中,方法如下:

public void addInterceptor(HandlerInterceptor interceptor) {
    initInterceptorList().add(interceptor);
}

private List<HandlerInterceptor> initInterceptorList() {
    // 如果 interceptorList 为空,则初始化为 ArrayList
    if (this.interceptorList == null) {
        this.interceptorList = new ArrayList<>();
        // 如果 interceptors 非空,则添加到 interceptorList 中
        if (this.interceptors != null) {
            // An interceptor array specified through the constructor
            CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
        }
    }
    // 置空 interceptors
    this.interceptors = null;
    // 返回 interceptorList
    return this.interceptorList;
}

getInterceptors

getInterceptors() 方法,获得 interceptors 数组,方法如下:

@Nullable
public HandlerInterceptor[] getInterceptors() {
    // 将 interceptorList 初始化到 interceptors 中
    if (this.interceptors == null && this.interceptorList != null) {
        this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
    }
    // 返回 interceptors 数组
    return this.interceptors;
}

applyPreHandle

applyPreHandle(HttpServletRequest request, HttpServletResponse response) 方法,执行请求匹配的拦截器的前置处理,方法如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // <1> 获得拦截器数组
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        // <2> 遍历拦截器数组
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            // <3> 前置处理
            if (!interceptor.preHandle(request, response, this.handler)) {
                // <3.1> 已完成处理 拦截器
                triggerAfterCompletion(request, response, null);
                // 返回 false ,前置处理失败
                return false;
            }
            // <3.2> 标记 interceptorIndex 位置
            this.interceptorIndex = i;
        }
    }
    // <4> 返回 true ,前置处理成功
    return true;
}
  1. 获得拦截器数组,通过上面的 getInterceptors() 方法,获得 interceptors 数组

  1. 遍历 interceptors 拦截器数组

  1. 依次执行拦截器的前置处理

  1. 如果有某个拦截器的前置处理失败,则调用 triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 方法,触发拦截器们的已完成处理,最后返回 false

  1. 每个拦截器成功执行前置处理后,记录当前拦截器的位置到 interceptorIndex 属性中,为了已完成处理只会执行前置处理成功的拦截器,且倒序执行

  1. 返回 true,拦截器们的前置处理都成功

applyPostHandle

applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) 方法,执行请求匹配的拦截器的后置处理,方法如下:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
        throws Exception {
    // 获得拦截器数组
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        // 遍历拦截器数组
        for (int i = interceptors.length - 1; i >= 0; i--) { // 倒序
            HandlerInterceptor interceptor = interceptors[i];
            // 后置处理
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
}
  • 请求匹配的拦截器的后置处理倒序执行的

  • 如果前置处理没有全部执行成功,或者处理请求的过程中出现异常是不会调用该方法的,也就是不会执行后置处理

triggerAfterCompletion

triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 方法,执行请求匹配的拦截器的已完成处理,方法如下:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
        throws Exception {
    // 获得拦截器数组
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        // 遍历拦截器数组
        for (int i = this.interceptorIndex; i >= 0; i--) { // 倒序!!!
            HandlerInterceptor interceptor = interceptors[i];
            try {
                // 已完成处理 拦截器
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) { // 注意,如果执行失败,仅仅会打印错误日志,不会结束循环
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}
  • 请求匹配的拦截器的已完成处理倒序执行的

  • 通过interceptorIndex属性,只会执行前置处理成功的拦截器们,因为该属性定义了成功执行前置处理的拦截器的位置

  • 如果前置处理没有全部执行成功,或者处理请求的过程中出现异常还是会调用该方法,也就是执行已完成处理

HandlerInterceptor 的实现类

HandlerMapping 接口体系的结构如下:

可以看到它的实现类有许多,这里来看几个重要的类

MappedInterceptor

org.springframework.web.servlet.handler.MappedInterceptor,实现 HandlerInterceptor 接口,支持地址匹配的 HandlerInterceptor 实现类

每一个 <mvc:interceptor /> 标签,将被解析成一个 MappedInterceptor 类型的 Bean 拦截器对象

构造方法
public final class MappedInterceptor implements HandlerInterceptor {
    /**
     * 匹配的路径
     */
    @Nullable
    private final String[] includePatterns;

    /**
     * 不匹配的路径
     */
    @Nullable
    private final String[] excludePatterns;

    /**
     * 拦截器对象
     */
    private final HandlerInterceptor interceptor;

    /**
     * 路径匹配器
     */
    @Nullable
    private PathMatcher pathMatcher;

    public MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor) {
        this(includePatterns, null, interceptor);
    }
    
    public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
            HandlerInterceptor interceptor) {
        this.includePatterns = includePatterns;
        this.excludePatterns = excludePatterns;
        this.interceptor = interceptor;
    }

    public MappedInterceptor(@Nullable String[] includePatterns, WebRequestInterceptor interceptor) {
        this(includePatterns, null, interceptor);
    }
}
  • includePatterns:拦截器需要匹配的请求路径

  • excludePatterns:拦截器需要排除的请求路径

  • pathMatcher:路径匹配器

  • interceptor:拦截器对象

通过前面三个属性去判断请求是否匹配

matches

matches(String lookupPath, PathMatcher pathMatcher) 方法,判断请求路径是否匹配,方法如下:

public boolean matches(String lookupPath, PathMatcher pathMatcher) {
    PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
    // <1> 先判断该路径是否在不匹配的路径中
    if (!ObjectUtils.isEmpty(this.excludePatterns)) {
        for (String pattern : this.excludePatterns) {
            if (pathMatcherToUse.match(pattern, lookupPath)) {
                return false;
            }
        }
    }
    // <2> 如果匹配的路径为空,则都匹配通过
    if (ObjectUtils.isEmpty(this.includePatterns)) {
        return true;
    }
    // <3> 判断路径是否在需要匹配的路径中
    for (String pattern : this.includePatterns) {
        if (pathMatcherToUse.match(pattern, lookupPath)) {
            return true;
        }
    }
    return false;
}
  1. 先判断该路径是否在不匹配的路径中

  1. 如果匹配的路径为空,则都匹配通过

  1. 判断路径是否在需要匹配的路径中

拦截方法的实现
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return this.interceptor.preHandle(request, response, handler);
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    this.interceptor.postHandle(request, response, handler, modelAndView);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    this.interceptor.afterCompletion(request, response, handler, ex);
}

都是直接调用interceptor拦截器对应的方法

其他

使用示例

1. <mvc:interceptors> 标签

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/error/**" />
        <bean class="com.tiger.study.interceptor.JwtInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>
  • 每一个 <mvc:interceptor /> 标签,将被解析成一个 MappedInterceptor 类型的 Bean 拦截器对象

  • 然后 MappedInterceptor 类型的拦截器在 AbstractHandlerMapping 的 initApplicationContext() -> detectMappedInterceptors 会被扫描到

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    // 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中
    // MappedInterceptor 会根据请求路径做匹配,是否进行拦截
    mappedInterceptors.addAll(BeanFactoryUtils
            .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false)
            .values());
}

也就是说在初始化 HandlerMapping 组件的时候会扫描到我们自定义的拦截器,并添加到属性中

<mvc:interceptor /> 标签如何被解析成MappedInterceptor对象的?

可以来看下spring-webmvc工程的 spring.handlers 文件,如下:

指定了 NamespaceHandler 为 MvcNamespaceHandler 对象,也就是说<mvc />标签会被该对象进行解析,如下:

public class MvcNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
        registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
        registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
        registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
        registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
    }
}

其中<mvc:interceptor />标签则会被 InterceptorsBeanDefinitionParser 对象进行解析,如下:

class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
    @Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext context) {
        context.pushContainingComponent(
                new CompositeComponentDefinition(element.getTagName(), context.extractSource(element)));

        RuntimeBeanReference pathMatcherRef = null;
        if (element.hasAttribute("path-matcher")) {
            pathMatcherRef = new RuntimeBeanReference(element.getAttribute("path-matcher"));
        }

        List<Element> interceptors = DomUtils.getChildElementsByTagName(element, "bean", "ref", "interceptor");
        for (Element interceptor : interceptors) {
            // 将 <mvc:interceptor /> 标签解析 MappedInterceptor 对象
            RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
            mappedInterceptorDef.setSource(context.extractSource(interceptor));
            mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

            ManagedList<String> includePatterns = null;
            ManagedList<String> excludePatterns = null;
            Object interceptorBean;
            if ("interceptor".equals(interceptor.getLocalName())) {
                includePatterns = getIncludePatterns(interceptor, "mapping");
                excludePatterns = getIncludePatterns(interceptor, "exclude-mapping");
                Element beanElem = DomUtils.getChildElementsByTagName(interceptor, "bean", "ref").get(0);
                interceptorBean = context.getDelegate().parsePropertySubElement(beanElem, null);
            }
            else {
                interceptorBean = context.getDelegate().parsePropertySubElement(interceptor, null);
            }
            mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, includePatterns);
            mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, excludePatterns);
            mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(2, interceptorBean);

            if (pathMatcherRef != null) {
                mappedInterceptorDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
            }

            String beanName = context.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
            context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName));
        }

        context.popAndRegisterContainingComponent();
        return null;
    }

    private ManagedList<String> getIncludePatterns(Element interceptor, String elementName) {
        List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, elementName);
        ManagedList<String> patterns = new ManagedList<>(paths.size());
        for (Element path : paths) {
            patterns.add(path.getAttribute("path"));
        }
        return patterns;
    }
}

逻辑不复杂,会将 <mvc:interceptor /> 标签解析 BeanDefinition 对象,beanClass 为 MappedInterceptor,解析出来的属性也会添加至其中,也就会给初始化成 MappedInterceptor 类型的 Spring Bean 到 Spring 上下文中

2. Java Config

在 SpringBoot 2.0+ 项目中,添加拦截器的方式可以如下:

@Component
public class JwtInterceptor implements HandlerInterceptor {
    /**
     * 前置处理
     *
     * @param handler  拦截的目标,处理器
     * @return 该请求是否继续往下执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // JWT 校验
        // 验证通过,返回 true,否则返回false
        return true;
    }
    /** 后置处理 */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 
        throws Exception {
    }
    /** 已完成处理 */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
        throws Exception {
    }
}

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        // 将拦截器添加至 InterceptorRegistry
        registry.addInterceptor(jwtInterceptor()).addPathPatterns("/**").excludePathPatterns(excludePath);
    }

    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }

}
  • 使用的过程中,如果patterns路径没有设置好,可能在请求过程中发生的错误会被拦截器拦截到,可以在拦截器中根据自定义注解进行拦截处理

因为 JwtInterceptor 不是 MappedInterceptor 类型的拦截器,不会被 AbstractHandlerMapping 探测到,既然这样子,那么我们就直接调用 AbstractHandlerMapping 的 setInterceptors(Object... interceptors) 设置进去不就好了

由于 Spring 5.0 废弃了 WebMvcConfigurerAdapter,所以需要通过 WebMvcConfigurer 接口来添加我们的拦截器,那么在 Spring Boot 2.0+ 中是如何将 WebMvcConfigurer 添加的拦截器设置到 AbstractHandlerMapping 对象中的呢?接下来开始简单的分析


先来看到 spring-boot-autoconfigure 项目中的 WebMvcAutoConfiguration 自动配置类,其中有一个内部静态类 EnableWebMvcConfiguration,继承了 DelegatingWebMvcConfiguration 对象(spring-webmvc 项目中),部分代码如下:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    // ... 省略相关代码
}

回到我们的 spring-webmvc项目,来看到 org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration 这个类,继承 WebMvcConfigurationSupport 类,部分代码如下

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    /** WebMvcConfigurer 组合类,内部方法就是遍历所有的 WebMvcConfigurer 实现类 */
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            // <1> 注入所有的 WebMvcConfigurer 实现类到 configurers 中
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
    
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        // <2> 调用 WebMvcConfigurer 组合类的 addInterceptors(InterceptorRegistry registry) 方法
        this.configurers.addInterceptors(registry);
    }
}

// org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite.java
class WebMvcConfigurerComposite implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // <3> 依次执行 WebMvcConfigurer 实现类的 addInterceptors 方法,将对应的拦截器添加至 registry 中
        for ( WebMvcConfigurer delegate : this.delegates) {
            delegate.addInterceptors(registry);
        }
    }
}
  1. 注入所有的 WebMvcConfigurer 实现类到 configurers 中,示例中我们自定义的 InterceptorConfig 就会被注入到这里

  1. 调用 WebMvcConfigurer 组合类的 addInterceptors(InterceptorRegistry registry) 方法,看第 3

  1. 依次执行 WebMvcConfigurer 实现类的 addInterceptors(InterceptorRegistry registry) 方法,将对应的拦截器添加至 registry 中。调用示例中我们自定义的 InterceptorConfig 方法,则将我们自定义 JwtInterceptor 拦截器添加至 registry 中了

再来看到 org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport 这个类,部分代码如下:

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
        mapping.setOrder(0);
        mapping.setInterceptors(getInterceptors());
        // ... 省略相关代码
        return mapping;
    }
    
    protected final Object[] getInterceptors() {
        // 若 interceptors 未初始化,则进行初始化
        if (this.interceptors == null) {
            // 创建 InterceptorRegistry 对象
            InterceptorRegistry registry = new InterceptorRegistry();
            // 添加拦截器到 interceptors 中
            addInterceptors(registry);
            // 添加内置拦截器到 interceptors 中
            registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
            registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
            // 初始化到 interceptors 属性
            this.interceptors = registry.getInterceptors();
        }
        // 若 interceptors 已初始化,则直接返回
        return this.interceptors.toArray();
    }
}

逻辑并不复杂,可以看到 Spring MVC 用到的 RequestMappingHandlerMapping 对象会通过 addInterceptors(InterceptorRegistry registry) 方法,获取到我们自定义InterceptorConfig中添加的JwtInterceptor 拦截器,并设置到 RequestMappingHandlerMapping 对象中

总结

本文对 Spring MVC 处理请求的过程中使用到的 HandlerMapping 组件中的 HandlerInterceptor 拦截器进行了分析,DispatcherServlet 在处理请求的过程中,会执行 HandlerMapping 组件中与请求匹配的拦截器,进行一些拦截处理。拦截器的在项目中会经常使用到,应用场景比较多,例如权限校验、参数预处理等等,上面也提供了相应的使用示例

拦截器有以下三个方法:

  • preHandle:前置处理,在执行方法前执行,全部成功执行才会往下执行方法

  • postHandle:后置处理,在成功执行方法后执行,倒序

  • afterCompletion:已完成处理,不管方法是否成功执行都会执行,不过只会执行前置处理成功的拦截器,倒序

多个拦截器的执行顺序就是自定义 WebMvcConfigurer 实现类添加拦截器时所加入的顺序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/362082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

复旦大学邱锡鹏团队发布类 ChatGPT 模型MOSS

不知道这个人工智能&#xff0c;有没有获得完整的一生。 ChatGPT 是最先进的 AI&#xff0c;也是最热门的应用 —— 自去年 11 月底发布以来&#xff0c;它的月活跃用户两个月超过一亿&#xff0c;轻松拿到了全球互联网史上用户增长速度的第一。 它也是一种门槛很高的技术。由…

Metasploit 使用篇(二)

文章目录前言一、侦察1.banner信息2.http头检测小结二、枚举DNS枚举枚举文件三、漏洞扫描四、漏洞评估前言 内容均来自《Web渗透测试实战 基于Metasploit5.0》&#xff0c;包括渗透测试生命周期中的4个部分&#xff1a; 侦察Web应用枚举漏洞扫描漏洞评估 以对metasplitable …

入门力扣自学笔记236 C++ (题目编号:1326)(贪心算法)

1326. 灌溉花园的最少水龙头数目 题目&#xff1a; 在 x 轴上有一个一维的花园。花园长度为 n&#xff0c;从点 0 开始&#xff0c;到点 n 结束。 花园里总共有 n 1 个水龙头&#xff0c;分别位于 [0, 1, ..., n] 。 给你一个整数 n 和一个长度为 n 1 的整数数组 ranges …

江南爱窗帘十大品牌 | 推荐3种简单的窗帘上色方法

窗帘可以在色彩搭配好的空间里营造温度和情调&#xff1f;窗帘不仅是用来挡光的&#xff0c;更能营造温暖和氛围家。除了成品家具&#xff0c;家中软装的另一大部分就是窗帘了。窗帘的颜色搭配好&#xff0c;我们家瞬间有温度。今天我们将讨论一些简单的窗帘上色方法。1、窗帘我…

计算机网络基础知识--数据链路层协议

目录 使用点对点信道的数据链路层 数据链路层的三个基本问题 封装成帧 透明传输 差错检测 点对点协议PPP PPP协议的组成 PPP帧的格式 数据链路层的协议数据单元是帧。数据链路层把网络层交下来的数据构成帧发送到链路上&#xff0c;以及把接收到的帧中的数据取出并上交网…

VR全景带你打卡《狂飙》经典取景地!

热度“狂飙”&#xff01;电视剧《狂飙》的取景地——江门墟顶老街人气火爆&#xff0c;720VR全景带您了解&#xff0c;这个具有新活力的老街区&#xff0c;蛙色3DVR提供技术支持&#xff01;通过航拍VR全景&#xff0c;全方位展示江门历史文化街区&#xff0c;720浏览&#xf…

3 nacos与其他注册中心对比以及提升

对比如下&#xff1a; CAP模型&#xff1a; 计算机专家 埃里克布鲁尔&#xff08;Eric Brewer&#xff09;于 2000 年在 ACM 分布式计算机原理专题讨论会&#xff08;简称&#xff1a;PODC&#xff09;中提出的分布式系统设计要考虑的三个核心要素&#xff1a; (1) 一致性&am…

appium自动化测试

获取应用包名和入口activity&#xff1a;aapt命令 aapt目录&#xff1a; 安卓sdk的build-tools目录下(如果要在cmd里直接运行&#xff0c;要配置环境变量&#xff0c;否则需要在aapt所在目录下打开cmd) 示例&#xff1a; adt-bundle-windows-x86_64-20140702\sdk\build-too…

微信小程序开发(二)

一、封装request.js请求文件 目的&#xff1a;优化代码结构以及后期项目版本迭代和维护更加方便&#xff0c;提升代码的执行速度。 假设在原生page中使用基本写法创建ajax请求&#xff0c;则会出现以下问题&#xff1a; 1、page界面业务操作代码混乱&#xff0c;代码多层嵌套…

Hudi(26):Hudi集成Hive之同步工具

目录 0. 相关文章链接 1. 同步工具概述 2. 使用语法及参数 2.1. 语法 2.2. 参数说明 3. 解决依赖问题 3.1. 解决点一&#xff1a;修改hadoop、hive、hudi-hive-sync-bundle-0.12.0.jar的依赖加载 3.2. 解决点二&#xff1a;解决parquet-column的版本冲突 4. JDBC模式同…

buu [WUSTCTF2020]dp_leaking_1s_very_d@angerous 1

题目描述&#xff1a; e 65537 n 1568083435985787749573756968151889806821667406093028310996964920682463371987925108988184962391663390152073051021014316342831685444929845865667999964711502523821441482572367072472675061656708775063702531276953141639870840764…

Docker竟如此简单!

文章目录什么是容器&#xff1f;容器隔离何为“边界”&#xff1f;容器和虚拟机一样吗&#xff1f;基于 Linux Namespace 隔离机制的弊端容器限制何为“限制”&#xff1f;Cgroups 对资源的限制能力缺陷单进程模型容器镜像容器的诞生容器的一致性何为“层&#xff08;layer&…

前端必须知道的http知识

HTTP协议也叫超文本传输协议&#xff0c;是一种基于TCP/IP的应用层通信协议&#xff0c;这个协议详细规定了浏览器和万维网服务器之间互相通信的规则&#xff08;报文&#xff0c;请求报文、响应报文&#xff09; 请求方式 HTTP设定了八种发送请求方式&#xff0c;这八种方法没…

SpringCloud(二)配置中心

配置中心Nacos配置中心多环境共享Nacos集群搭建Nacos配置中心 作用&#xff1a; 统一配置管理配置自动刷新&#xff0c;热更新 实现&#xff1a; 统一配置管理 在nacos服务端&#xff0c;配置管理配置列表中新建配置了解配置获取的步骤&#xff1a; 项目启动->读取nacos中…

MySQL中有多少种索引?索引的底层实现原理

索引存储在内存中&#xff0c;为服务器存储引擎为了快速找到记录的一种数据结构。索引的主要作用是加快数据查找速度&#xff0c;提高数据库的性能。索引的分类(1) 普通索引&#xff1a;最基本的索引&#xff0c;它没有任何限制。(2) 唯一索引&#xff1a;与普通索引类似&#…

Java技术之注解

前言 Java的反射和注解是Java和Android程序员必须掌握的技术&#xff0c;注解使程序员可以将重复冗余的工作封装到一个框架里面&#xff0c;提供一个简单的API接口给程序使用&#xff0c;典型的有黄油刀(ButterKnife)、ARouter、EventBus等都使用了注解。这些优秀的框架都是使…

win7下安装postgreSQL教程

系统环境&#xff1a;Windows 7 旗舰版 64位操作系统 安装版本&#xff1a;postgresql-9.1.4-1-windows-x64 安装步骤&#xff1a; 1、下载系统对应的软件版本&#xff1b; 2、双击“postgresql-9.1.4-1-windows-x64.exe”打开安装窗口&#xff1b; 3、Welcome页&#xff0c;…

ChatGPT来了,英语不能丢,但我不想上班

文 / 谷雨&#xff08;微信公众号&#xff1a;王不留&#xff09; 好久没写文&#xff0c;可能大伙已把我忘了。春节之后&#xff0c;状态一直不太好。我在2月1号时从老家直接来到了深圳出差&#xff0c;而后以996的工作状态疲于应付工作中的各种问题。 终于这周末休息了两天&a…

【linux安装redis详解】小白如何安装部署redis,linux安装部署只需5步骤(图文结合,亲测有效)

【写在前面】前端时间接触了一下redis&#xff0c;也是迫于页面查询响应太慢&#xff0c;没办法听说redis这个可持久化内存数据库&#xff0c;于是乎便想着在自己的机器上安装一套&#xff0c;接下来就重点和大家说说怎么从小白开始摸索redis 目录1、下载2、安装2.1 创建文件存…

全开源无加密的RuleApp文章社区APP客户端源码

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 开源无加密的文章社区客户端源码分享 RuleApp文章社区&#xff0c;VIP会员&#xff0c;写作投稿积分商城&#xff0c;付费模块集成&#xff0c;多平台兼容这是一款开源免费&#xff0c;界…