SpringBoot MVC(9)拦截器与MVC总结

2,064 阅读3分钟

拦截器,英文名为Interceptor,在项目中可以用来做登录和权限的拦截,下面将从实际应用出发,来探索它的实现。

代码准备

拦截器bean

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        // 这里的handler一般为HandlerMethod类,可以通过它得知当前运行的类和方法相关信息,以此来处理一些业务的逻辑
        System.out.println("login");
        return true;
    }
}
@Component
public class PermissionInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                        Object handler) throws Exception {
        System.out.println("permission");
        return true;
    }
}

配置类

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Resource
    private LoginInterceptor loginInterceptor;
    @Resource
    private PermissionInterceptor permissionInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(permissionInterceptor).addPathPatterns("/hello/test/**");
        registry.addInterceptor(loginInterceptor).addPathPatterns("/hello/demo/**");

    }
}

启动阶段

beanName为org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration对应的类为WebMvcAutoConfiguration.EnableWebMvcConfiguration,它继承了父类DelegatingWebMvcConfiguration,如下图,在依赖注入阶段,会添加IoC容器中的WebMvcConfigurer类到configurers变量,并最终在delegates变量体现。这就是配置类必须要实现WebMvcConfigurer接口的原因了

在实例化beanName为requestMappingHandlerMapping的时候,其对应类为RequestMappingHandlerMapping,会走入以下方法中。

WebMvcAutoConfiguration->EnableWebMvcConfiguration->requestMappingHandlerMapping():
    // 执行的是父类的requestMappingHandlerMapping()
    return super.requestMappingHandlerMapping();
WebMvcConfigurationSupport->requestMappingHandlerMapping():
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    // 设置拦截器
    mapping.setInterceptors(getInterceptors());
    mapping.setContentNegotiationManager(mvcContentNegotiationManager());
    // Cors,如果配置类有重写addCorsMappings方法,将会添加
    mapping.setCorsConfigurations(getCorsConfigurations());
WebMvcConfigurationSupport->getInterceptors():
    InterceptorRegistry registry = new InterceptorRegistry();
    // 添加拦截器
    addInterceptors(registry);
    // 再添加两个默认的拦截器
    registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
    registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
    this.interceptors = registry.getInterceptors();
DelegatingWebMvcConfiguration->addInterceptors():
    this.configurers.addInterceptors(registry);
WebMvcConfigurerComposite->addInterceptors():
    // 这里的delegates一个是默认的拦截器,一个是配置的拦截器
    for (WebMvcConfigurer delegate : this.delegates) {
        delegate.addInterceptors(registry);
    }

这里的delegates一个是默认的配置类WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter,它的addInterceptors方法为空。另一个是配置的InterceptorConfig,执行到其下的addInterceptors方法时,添加了指定的两个拦截器,之后还会再添加两个默认的拦截器,最终的体现就是requestMappingHandlerMappinginterceptors变量。

实例化RequestMappingHandlerMapping之后,在初始化阶段,还会做映射的查找与注册

运行阶段

处理方法与拦截器的指定

AbstractHandlerMapping->getHandler():
    Object handler = getHandlerInternal(request);
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
AbstractHandlerMapping->getHandlerExecutionChain():
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            // 当url符合配置的拦截器incluePatterns时,会添加
            // 符合excludePatterns时,不会添加
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        // 默认的两个是直接添加的
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;

Object handler = getHandlerInternal(request)根据不同的请求返回不同的处理方法(过程参考映射的匹配Servlet初始化中的常用handlerMapping介绍),之后在chain添加符合的拦截器链,返回此对象。

拦截器的调用

DispatcherServlet->doDispatch():
    // 调用各拦截器的preHandle方法,如果为false,调用afterCompletion方法之后返回
    // 这边判断为false直接return了
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    ......
    // 调用各拦截器的postHandle方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    ......
    // 最后,无论成功或是异常捕获,都会调用拦截器的afterCompletion方法

总结

HandlerInterceptor的三个方法在不同阶段被调用,最核心的还是它的preHandle方法,如果返回false,可以直接不执行接口方法,我想,这就是称其为“拦截器”的原因了吧。

SpringMVC流程图

从图中可以看出,过滤器(Filter)与拦截器(Interceptor)最大的区别是过滤器在Servlet作用之前