spring拦截器能帮我们实现验证是否登陆、验签校验请求是否合法、预先设置数据等功能,那么该如何设置拦截器以及它的原理如何呢,下面将进行简单的介绍
public interface HandlerInterceptor { /** * Intercept the execution of a handler. Called after HandlerMapping determined * an appropriate handler object, but before HandlerAdapter invokes the handler. * <p>DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can decide to abort the execution chain, * typically sending a HTTP error or writing a custom response. * <p><strong>Note:</strong> special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. * <p>The default implementation returns {@code true}. * @param request current HTTP request * @param response current HTTP response * @param handler chosen handler to execute, for type and/or instance evaluation * @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. * @throws Exception in case of errors */ default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } /** * Intercept the execution of a handler. Called after HandlerAdapter actually * invoked the handler, but before the DispatcherServlet renders the view. * Can expose additional model objects to the view via the given ModelAndView. * <p>DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can post-process an execution, * getting applied in inverse order of the execution chain. * <p><strong>Note:</strong> special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. * <p>The default implementation is empty. * @param request current HTTP request * @param response current HTTP response * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param modelAndView the {@code ModelAndView} that the handler returned * (can also be {@code null}) * @throws Exception in case of errors */ default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } /** * Callback after completion of request processing, that is, after rendering * the view. Will be called on any outcome of handler execution, thus allows * for proper resource cleanup. * <p>Note: Will only be called if this interceptor's {@code preHandle} * method has successfully completed and returned {@code true}! * <p>As with the {@code postHandle} method, the method will be invoked on each * interceptor in the chain in reverse order, so the first interceptor will be * the last to be invoked. * <p><strong>Note:</strong> special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. * <p>The default implementation is empty. * @param request current HTTP request * @param response current HTTP response * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param ex exception thrown on handler execution, if any * @throws Exception in case of errors */ default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
自定义拦截器需要实现HandlerInteceptor接口,该接口有三个方法:
preHandle:主要在映射适配器执行handler之前调用,若返回为true则继续往下执行handler,若返回为false则直接返回不继续处理请求
postHandle:主要在适配器执行handler之后调用
afterCompletion:在postHandle后调用可清理一些数据,若preHandle返回false那么会调用完此方法后再返回
@Component public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("-------------拦截请求:" + request.getRequestURI() + "-------------"); // 可以根据request设置请求头、或从请求头提取信息等等... return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("postHandle ...."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("afterCompletion ...."); } }
接着创建配置类,实现WebMvcConfigurer接口,重写addInterceptors方法将自定义拦截器添加,并且加上@EnableWebMvc注解 (springboot项目会自动配置)
@Configuration @EnableWebMvc public class MyMvcConfigurer implements WebMvcConfigurer { @Resource private CustomInterceptor customInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(customInterceptor) .addPathPatterns("/**"); } }
配置完之后启动项目访问某个url路径,从控制台可以看到拦截器确实生效了
首先是@EnableWebMvc注解,spring会解析并导入DelegatingWebMvcConfiguration这个bean,继承关系如下,主要逻辑都写在父类WebMvcConfigurationSupport中
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc { }
WebMvcConfigurationSupport中会创建一个映射处理器RequestMappingHandlerMapping
@Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); // 设置拦截器到mapping mapping.setInterceptors(getInterceptors()); // 设置内容协商管理器 mapping.setContentNegotiationManager(mvcContentNegotiationManager()); // 跨域配置 mapping.setCorsConfigurations(getCorsConfigurations()); // 路径匹配设置 PathMatchConfigurer configurer = getPathMatchConfigurer(); Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch(); if (useSuffixPatternMatch != null) { mapping.setUseSuffixPatternMatch(useSuffixPatternMatch); } Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch(); if (useRegisteredSuffixPatternMatch != null) { mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); } Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch(); if (useTrailingSlashMatch != null) { mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); } UrlPathHelper pathHelper = configurer.getUrlPathHelper(); if (pathHelper != null) { mapping.setUrlPathHelper(pathHelper); } PathMatcher pathMatcher = configurer.getPathMatcher(); if (pathMatcher != null) { mapping.setPathMatcher(pathMatcher); } return mapping; } #获取拦截器 protected final Object[] getInterceptors() { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); // 调用DelegatingWebMvcConfiguration.addInterceptors 添加自定义的拦截器 addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); // 获取拦截器并根据order排序,若有匹配路径则封装成MappedInterceptor this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
注意这一行代码mapping.setInterceptors(getInterceptors());
getInterceptors方法会调用子类DelegatingWebMvcConfiguration的addInterceptors方法,接着会调用委托类即我们自定义配置类MyMvcConfigurer类的addInterceptors方法,将自定义的拦截器添加到拦截器注册类中,而后通过拦截器注册类获取到拦截器列表,最后将拦截器添加到映射处理器handlerMapping中,供后续使用。
最后看下请求处理的DispatcherServlet#doDispatch方法 (为了看的更清楚一点删掉了一些代码)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; // 处理程序执行链 HandlerExecutionChain mappedHandler = null; try { ModelAndView mv = null; Exception dispatchException = null; try { // Determine handler for the current request. // 遍历handlerMapping获取能处理request的处理器,mappedHandler里封装着之前我们定义的拦截器供后续调用 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. // 确定处理当前请求的处理适配器 RequestMappingHandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 执行handler之前应用拦截器执行拦截器的后置方法 返回为false表示请求不合理直接返回了 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 真正执行这个HandlerMethod mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); applyDefaultViewName(processedRequest, mv); // 执行拦截器的后置方法 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); } 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 { } } #mappedHandler.applyPreHandle boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; // 前置处理为false时 if (!interceptor.preHandle(request, response, this.handler)) { // 触发拦截器的afterCompletion方法 triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
可以看到再真正执行handler之前会调用mappedHandler.applyPreHandle
方法,遍历拦截器执行preHandle方法,若返回false则根据先前执行过的拦截器顺序倒序执行afterCompletion方法,都通过的话后续执行handler获取请求结果,再接着执行拦截器的postHandle方法最后执行afterCompletion方法。
原文出处:SpringWeb 拦截器