注册到容器
springboot在启动的时候,加载配置类
WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
向容器注册了
@Bean@Primary@Overridepublic RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {// Must be @Primary for MvcUriComponentsBuilder to workreturn super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,resourceUrlProvider);}
真正的方法在父类
@Bean@SuppressWarnings("deprecation")public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();mapping.setOrder(0);//设置了拦截器mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));mapping.setContentNegotiationManager(contentNegotiationManager);mapping.setCorsConfigurations(getCorsConfigurations());PathMatchConfigurer pathConfig = getPathMatchConfigurer();if (pathConfig.getPatternParser() != null) {mapping.setPatternParser(pathConfig.getPatternParser());}else {mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();if (useSuffixPatternMatch != null) {mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);}Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();if (useRegisteredSuffixPatternMatch != null) {mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);}}Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();if (useTrailingSlashMatch != null) {mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);}if (pathConfig.getPathPrefixes() != null) {mapping.setPathPrefixes(pathConfig.getPathPrefixes());}return mapping;}
调用了同类的getInterceptors方法,跟进去
/*** Provide access to the shared handler interceptors used to configure* {@link HandlerMapping} instances with.* <p>This method cannot be overridden; use {@link #addInterceptors} instead.*/protected final Object[] getInterceptors(FormattingConversionService mvcConversionService,ResourceUrlProvider mvcResourceUrlProvider) {if (this.interceptors == null) {InterceptorRegistry registry = new InterceptorRegistry();addInterceptors(registry);registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));this.interceptors = registry.getInterceptors();}return this.interceptors.toArray();}
有2个默认的拦截器是springboot帮我们new好的;自定义拦截器是通过方法addInterceptors(registry)添加进去的,继续跟
#DelegatingWebMvcConfiguration#@Overrideprotected void addInterceptors(InterceptorRegistry registry) {this.configurers.addInterceptors(registry);}#WebMvcConfigurerComposite#private final List<WebMvcConfigurer> delegates = new ArrayList<>();@Overridepublic void addInterceptors(InterceptorRegistry registry) {for (WebMvcConfigurer delegate : this.delegates) {delegate.addInterceptors(registry);}}
这里的delegates包括:
- springboot帮我们配置好的
- WebMvcAutoConfiguration里面的静态内部配置类WebMvcAutoConfigurationAdapter implements WebMvcConfigurer
我们自己去实现这个接口的配置类,可以有多个噢
@Configurationpublic class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/");}}
嵌入流程
请求所有的入口都是DispatcherServlet#doDispatch
三个方法preHandle;postHandle;afterCompletion@Slf4jpublic class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();log.info("preHandle拦截的请求路径是{}",requestURI);//登录检查逻辑HttpSession session = request.getSession();Object loginUser = session.getAttribute("loginUser");if(loginUser != null)return true;request.setAttribute("msg","请先登录");//拦截住。未登录。跳转到登录页request.getRequestDispatcher("/").forward(request,response);return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
执行的在哪里呢 ```java
DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;
try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.【把注册的拦截器看看是否匹配,然后放在HandlerExecutionChain里面】mappedHandler = getHandler(processedRequest);......代码省略// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());......代码省略【详见下面具体的源码,执行前置处理】if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());......代码省略【详见下面具体的源码,执行后置处理】mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}//里面调用了mappedHandler.triggerAfterCompletion(request, response, null);、、【详见下面具体的源码,执行完成处理】processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {【详见下面具体的源码,执行完成处理】triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {【详见下面具体的源码,执行完成处理】triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {......代码省略}}
//1.得到请求处理,2.并且也得到对应的拦截器 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
```java#RequestMappingInfoHandlerMappingpublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {Object handler = getHandlerInternal(request); //1.得到请求处理,匹配请求urlHandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//2.并且也得到对应的拦截器return executionChain;}protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));for (HandlerInterceptor interceptor : this.adaptedInterceptors) {if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;【匹配了就加进去】if (mappedInterceptor.matches(request)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;}
#HandlerExecutionChainprivate final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();private int interceptorIndex = -1;/*** Apply preHandle methods of registered interceptors.* @return {@code true} if the execution chain should proceed with the* next interceptor or the handler itself. Else, DispatcherServlet assumes* that this interceptor has already dealt with the response itself.* 正序处理,并记录执行的拦截器顺序索引*/boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;}/*** Apply postHandle methods of registered interceptors.* 成功执行了目标方法以后,才会执行后置拦截器,倒序执行*/void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}/*** Trigger afterCompletion callbacks on the mapped HandlerInterceptors.* Will just invoke afterCompletion for all interceptors whose preHandle invocation* has successfully completed and returned true.* 要前置处理成功执行的,索引记录的,才会执行完成处理器,有可能目标方法没有执行,* 比如目标方法报错,前置报错,后置报错都会触发完成处理器*/void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}
拦截器原理用一张图概括
