注册拦截器原理
配置拦截器
在介绍底层添加拦截器之前,首先简单介绍一下如何添加拦截器。我们只需要一个实现HandlerInterceptor接口的类并在MVC配置类中通过重写addInterceptors方法将该类添加到拦截器中。
- 实现HandlerInterceptor
public class LoginHandlerInterceptor implements HandlerInterceptor {
//用于拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if(user == null){
request.setAttribute("msg", "No authority. Please sign in first");
request.getRequestDispatcher("/login").forward(request, response); //请求转发
return false;
}else{
return true;
}
}
//Controller方法处理完之后,DispatcherServlet进行视图渲染之前;可以对ModelAndView进行操作
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
//DispatcherServlet进行视图的渲染之后;多用于清理资源
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 添加拦截器配置
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//"/**"默认拦截所有请求,会将静态资源也拦截
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/login", "/", "/user/login", "/asserts/**");
}
}
那么springboot又是如何将我们自定义的拦截器添加进来的呢?
底层添加拦截器的主要的步骤:
下面将结合源码说明底层添加拦截器的原理;
说明:为方便讲解,展示的源码只包含部分相关的内容。
首先,SpringBoot会引入很多自动配置类(xxxAutoConfiguraion),WebMvcAutoConfiguration就是关于mvc配置的,会给容器添加很多mvc相关的组件;
RequestMappingHandlerMapping组件包含了拦截器,在注册这个组件时会调用超类的requestMappingHandlerMapping()方法来注册这个对象
public class WebMvcAutoConfiguration {
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager")
ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService")
FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider")
ResourceUrlProvider resourceUrlProvider) {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}
}
}
看一看继承关系:
这个方法最终会跳转到WebMvcConfigurationSupport中,该类是提供Java config配置MVC功能的主要类;
WebMvcConfigurationSupport创建RequestMappingHandlerMapping组件主要做了这几件事:
-
首先会使用createRequestMappingHandlerMapping()方法创建RequestMappingHandlerMapping对象;
-
然后通过mapping.setInterceptors(getInterceptors())为该对象添加拦截器;
-
getInterceptors()方法会返回所有拦截器;那么拦截器是从哪里扫描到的呢?
-
getInterceptors()方法会先获得一个InterceptorRegistry对象,该对象会调用addInterceptor方法将自定义添加的以及从mvcConversionService和mvcResourceUrlProvider获得的拦截器添加进来,并最后将所有拦截器返回;
//这是提供通过Java config配置MVC的主要类
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
//将获取到的拦截器添加到RequestMappingHandlerMapping
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
return mapping;
}
//获取拦截器
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
//通过重写该方法给registry中添加自定义的interceptor
addInterceptors(registry);
//mvcConversionService和mvcResourceUrlProvider中的拦截器
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
//通过重写此方法,方法体中加入registry.addInterceptors来添加自定义拦截器
protected void addInterceptors(InterceptorRegistry registry) {
}
而我们是通过重写WebMvcConfigurer的addInterceptors方法来添加拦截器的,又是怎么添加到WebMvcConfigurationSupport中的呢?
我们再来看看WebMvcConfigurationSupport中addInterceptors的调用过程:
addInterceptors首先被它的子类DelegatingWebMvcConfiguration重写了:
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
}
该类中,又会去配置类引入组件的类WebMvcConfigurerComposite中调用addInterceptors方法;
class WebMvcConfigurerComposite implements WebMvcConfigurer {
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
而我们的mvc配置类,正好是实现WebMvcConfigurer接口的类,我们添加拦截器就是重写addInterceptors()方法:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//"/**"默认拦截所有请求,会将静态资源也拦截
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/login", "/", "/user/login", "/asserts/**");
}
}
那么在delegate.addInterceptors(registry)时就会调用我们自己重写的方法;
因此,我们在mvc配置类中重写addInterceptors方法,会被添加进来,最终被getInterceptors方法获取到,添加到RequestMappingHandlerMapping中;
总结:
- SpringBoot自动配置类WebMvcAutoConfiguration通过调用超类(WebMvcConfigurationSupport)注册RequestMappingHandlerMapping;
- WebMvcConfigurationSupport将获得的RequestMappingHandlerMapping对象添加拦截器:mapping.setInterceptors(getInterceptors);
- getInterceptors首先会创建一个InterceptorRegistry对象,用于注册拦截器;
- 然后给registry添加拦截器:
-
添加自定义拦截器(实现HandlerInterceptor的类):addInterceptors(registry);
- 方法首先由它的子类DelegatingWebMvcConfiguration调用;
- 该类又会去配置类引入组件的类WebMvcConfigurerComposite中调用addInterceptors方法
- WebMvcConfigurerComposite会读取mvc配置类(继承WebMvcConfigurer)的信息,就会调用我们呢自己重写的addInterceptors
-
从mvcConversionService和mvcResourceUrlProvider获取拦截器,添加到registry中;
-
- 最后将registry中的拦截器加入到interceptors对象;