拦截器,英文名为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方法时,添加了指定的两个拦截器,之后还会再添加两个默认的拦截器,最终的体现就是requestMappingHandlerMapping的interceptors变量。
实例化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作用之前