一.引言
拦截器依赖于web框架,在SpringMVC中就是依赖SpringMVC框架,在实现上,基于java的反射机制,属于面向切面编程的一种应用,就是在Service方法或者一个方法前调用,或者一个方法后调用,甚至业务异常的时候做业务处理逻辑。比如动态代理就是拦截器的简单实现。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入进行一些业务操作,同时一个拦截器实例在一个Controller声明周期之内可以多次调用。但缺点是只能对Controller进行拦截,也可以拦击静态资源,必须要添加配置才可以避免静态资源被拦截,拦截器不能拦截的只有jsp。
二.过滤器和拦截器的调用顺序和执行流程
a. 执行顺序
- 过滤前
- 拦截前
- Action处理
- 拦截后
- 过滤后
b.执行流程
二. 实现
-
定义拦截器
public class PmsInterceptor extends HandlerInterceptorAdapter { } @Component @Slf4j public class AppContextInterceptor extends PmsInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { AppContext appContext = new AppContext(); String token = request.getHeader("token"); appContext.setToken(token); appContext.setIp(RequestUtil.getRealIp()); if(StringUtils.isNotEmpty(token)){ UserInfo user = getUserFromToken(token); if(user != null){ appContext.setUserId(user.getUserId()); appContext.setUserName(user.getUserName()); } } request.setAttribute(Constants.APP_CONTEXT, appContext); return super.preHandle(request, response, handler); } }
-
注册拦截器
@Configuration @Slf4j public class MvcConfig implements WebMvcConfigurer { @Resource List<PmsInterceptor> pmsInterceptorList; @Override public void addInterceptors(InterceptorRegistry registry) { if(CollectionUtils.isEmpty(pmsInterceptorList)){ return; } for(PmsInterceptor pmsInterceptor : pmsInterceptorList){ log.info("添加拦截器:{}", pmsInterceptor); registry.addInterceptor(pmsInterceptor); } WebMvcConfigurer.super.addInterceptors(registry); } }
三.源码解析
-
首先springboot会自动引入WebAutoConfiguration,在这个类中会通过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); } } }
-
实际调用WebMvcAutoConfiguration的超类WebMvcConfigurationSupport的requestMappingHandlerMapping方法
@Bean public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping(); mapping.setOrder(0); mapping.setInterceptors(this.getInterceptors(conversionService, resourceUrlProvider)); mapping.setContentNegotiationManager(contentNegotiationManager); mapping.setCorsConfigurations(this.getCorsConfigurations()); PathMatchConfigurer pathConfig = this.getPathMatchConfigurer(); Boolean useTrailingSlashMatch; if (pathConfig.getPatternParser() != null) { mapping.setPatternParser(pathConfig.getPatternParser()); } else { mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault()); mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault()); useTrailingSlashMatch = pathConfig.isUseSuffixPatternMatch(); if (useTrailingSlashMatch != null) { mapping.setUseSuffixPatternMatch(useTrailingSlashMatch); } Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch(); if (useRegisteredSuffixPatternMatch != null) { mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); } } useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch(); if (useTrailingSlashMatch != null) { mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); } if (pathConfig.getPathPrefixes() != null) { mapping.setPathPrefixes(pathConfig.getPathPrefixes()); } return mapping; } protected final Object[] getInterceptors(FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); this.addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService)); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider)); this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
- 通过createRequestMappingHandlerMapping方法创建一个RequestMappingHandlerMapping对象
- 然后通过getInterceptors获取所有拦截器
- getInterceptors先注册一个interceptorRegistry对象,该对象会调用addInterceptor方法将自定义添加的以及从mvcConversionService和mvcResourceUrlProvider获得的拦截器添加进来,并最后将所有拦截器返回
- 为RequestMappingHandlerMapping对象设置一个拦截器
-
我们通过重写WebMvcConfigurer的addInterceptors方法来添加拦截器的getInterceptors方法怎么获取拦截器的,又是怎么添加到WebMvcConfigurationSupport中的?首先WebMvcConfigurationSupport被子类DelegatingWebMvcConfiguration重写了
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); @Override protected void addInterceptors(InterceptorRegistry registry) { this.configurers.addInterceptors(registry); } }
-
DelegatingWebMvcConfiguration调用addInterceptors时调用了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接口的类,我们添加拦截器就是重写addInterceptor方法
@Configuration @Slf4j public class MvcConfig implements WebMvcConfigurer { @Resource List<PmsInterceptor> pmsInterceptorList; @Override public void addInterceptors(InterceptorRegistry registry) { if(CollectionUtils.isEmpty(pmsInterceptorList)){ return; } for(PmsInterceptor pmsInterceptor : pmsInterceptorList){ log.info("添加拦截器:{}", pmsInterceptor); registry.addInterceptor(pmsInterceptor); } } }
注意:WebMvcConfigurationSupport类的getInterceptors和注册添加的一系列操作都是通过registry对象连接的。