SpringWeb 拦截器

打印 上一主题 下一主题

主题 531|帖子 531|积分 1593

前言

spring拦截器能帮我们实现验证是否登陆、验签校验请求是否合法、预先设置数据等功能,那么该如何设置拦截器以及它的原理如何呢,下面将进行简单的介绍
1.设置

HandlerInterceptor接口
  1. public interface HandlerInterceptor {
  2.         /**
  3.          * Intercept the execution of a handler. Called after HandlerMapping determined
  4.          * an appropriate handler object, but before HandlerAdapter invokes the handler.
  5.          * <p>DispatcherServlet processes a handler in an execution chain, consisting
  6.          * of any number of interceptors, with the handler itself at the end.
  7.          * With this method, each interceptor can decide to abort the execution chain,
  8.          * typically sending a HTTP error or writing a custom response.
  9.          * <p><strong>Note:</strong> special considerations apply for asynchronous
  10.          * request processing. For more details see
  11.          * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
  12.          * <p>The default implementation returns {@code true}.
  13.          * @param request current HTTP request
  14.          * @param response current HTTP response
  15.          * @param handler chosen handler to execute, for type and/or instance evaluation
  16.          * @return {@code true} if the execution chain should proceed with the
  17.          * next interceptor or the handler itself. Else, DispatcherServlet assumes
  18.          * that this interceptor has already dealt with the response itself.
  19.          * @throws Exception in case of errors
  20.          */
  21.         default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  22.                         throws Exception {
  23.                 return true;
  24.         }
  25.         /**
  26.          * Intercept the execution of a handler. Called after HandlerAdapter actually
  27.          * invoked the handler, but before the DispatcherServlet renders the view.
  28.          * Can expose additional model objects to the view via the given ModelAndView.
  29.          * <p>DispatcherServlet processes a handler in an execution chain, consisting
  30.          * of any number of interceptors, with the handler itself at the end.
  31.          * With this method, each interceptor can post-process an execution,
  32.          * getting applied in inverse order of the execution chain.
  33.          * <p><strong>Note:</strong> special considerations apply for asynchronous
  34.          * request processing. For more details see
  35.          * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
  36.          * <p>The default implementation is empty.
  37.          * @param request current HTTP request
  38.          * @param response current HTTP response
  39.          * @param handler handler (or {@link HandlerMethod}) that started asynchronous
  40.          * execution, for type and/or instance examination
  41.          * @param modelAndView the {@code ModelAndView} that the handler returned
  42.          * (can also be {@code null})
  43.          * @throws Exception in case of errors
  44.          */
  45.         default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  46.                         @Nullable ModelAndView modelAndView) throws Exception {
  47.         }
  48.         /**
  49.          * Callback after completion of request processing, that is, after rendering
  50.          * the view. Will be called on any outcome of handler execution, thus allows
  51.          * for proper resource cleanup.
  52.          * <p>Note: Will only be called if this interceptor's {@code preHandle}
  53.          * method has successfully completed and returned {@code true}!
  54.          * <p>As with the {@code postHandle} method, the method will be invoked on each
  55.          * interceptor in the chain in reverse order, so the first interceptor will be
  56.          * the last to be invoked.
  57.          * <p><strong>Note:</strong> special considerations apply for asynchronous
  58.          * request processing. For more details see
  59.          * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
  60.          * <p>The default implementation is empty.
  61.          * @param request current HTTP request
  62.          * @param response current HTTP response
  63.          * @param handler handler (or {@link HandlerMethod}) that started asynchronous
  64.          * execution, for type and/or instance examination
  65.          * @param ex exception thrown on handler execution, if any
  66.          * @throws Exception in case of errors
  67.          */
  68.         default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  69.                         @Nullable Exception ex) throws Exception {
  70.         }
  71. }
复制代码
自定义拦截器需要实现HandlerInteceptor接口,该接口有三个方法:
preHandle:主要在映射适配器执行handler之前调用,若返回为true则继续往下执行handler,若返回为false则直接返回不继续处理请求
postHandle:主要在适配器执行handler之后调用 
afterCompletion:在postHandle后调用可清理一些数据,若preHandle返回false那么会调用完此方法后再返回
  1. @Component
  2. public class CustomInterceptor implements HandlerInterceptor {
  3.   @Override
  4.   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  5.       throws Exception {
  6.     System.out.println("-------------拦截请求:" + request.getRequestURI() + "-------------");
  7.     // 可以根据request设置请求头、或从请求头提取信息等等...
  8.     return true;
  9.   }
  10.   @Override
  11.   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  12.       @Nullable ModelAndView modelAndView) throws Exception {
  13.     System.out.println("postHandle ....");
  14.   }
  15.   @Override
  16.   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  17.       @Nullable Exception ex) throws Exception {
  18.     System.out.println("afterCompletion ....");
  19.   }
  20. }
复制代码
接着创建配置类,实现WebMvcConfigurer接口,重写addInterceptors方法将自定义拦截器添加,并且加上@EnableWebMvc注解 (springboot项目会自动配置)
  1. @Configuration
  2. @EnableWebMvc
  3. public class MyMvcConfigurer implements WebMvcConfigurer {
  4.   @Resource
  5.   private CustomInterceptor customInterceptor;
  6.   @Override
  7.   public void addInterceptors(InterceptorRegistry registry) {
  8.     registry.addInterceptor(customInterceptor)
  9.         .addPathPatterns("/**");
  10.   }
  11. }
复制代码
配置完之后启动项目访问某个url路径,从控制台可以看到拦截器确实生效了

 
2.原理

首先是@EnableWebMvc注解,spring会解析并导入DelegatingWebMvcConfiguration这个bean,继承关系如下,主要逻辑都写在父类WebMvcConfigurationSupport中
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(DelegatingWebMvcConfiguration.class)
  5. public @interface EnableWebMvc {
  6. }
复制代码

WebMvcConfigurationSupport中会创建一个映射处理器RequestMappingHandlerMapping
  1. @Bean
  2. public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  3.         RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
  4.         mapping.setOrder(0);
  5.         // 设置拦截器到mapping
  6.         mapping.setInterceptors(getInterceptors());
  7.         // 设置内容协商管理器
  8.         mapping.setContentNegotiationManager(mvcContentNegotiationManager());
  9.         // 跨域配置
  10.         mapping.setCorsConfigurations(getCorsConfigurations());
  11.         // 路径匹配设置
  12.         PathMatchConfigurer configurer = getPathMatchConfigurer();
  13.         Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
  14.         if (useSuffixPatternMatch != null) {
  15.                 mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
  16.         }
  17.         Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
  18.         if (useRegisteredSuffixPatternMatch != null) {
  19.                 mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
  20.         }
  21.         Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
  22.         if (useTrailingSlashMatch != null) {
  23.                 mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
  24.         }
  25.         UrlPathHelper pathHelper = configurer.getUrlPathHelper();
  26.         if (pathHelper != null) {
  27.                 mapping.setUrlPathHelper(pathHelper);
  28.         }
  29.         PathMatcher pathMatcher = configurer.getPathMatcher();
  30.         if (pathMatcher != null) {
  31.                 mapping.setPathMatcher(pathMatcher);
  32.         }
  33.         return mapping;
  34. }
  35. #获取拦截器
  36. protected final Object[] getInterceptors() {
  37.         if (this.interceptors == null) {
  38.                 InterceptorRegistry registry = new InterceptorRegistry();
  39.                 // 调用DelegatingWebMvcConfiguration.addInterceptors 添加自定义的拦截器
  40.                 addInterceptors(registry);
  41.                 registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
  42.                 registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
  43.                 // 获取拦截器并根据order排序,若有匹配路径则封装成MappedInterceptor
  44.                 this.interceptors = registry.getInterceptors();
  45.         }
  46.         return this.interceptors.toArray();
  47. }
复制代码
注意这一行代码mapping.setInterceptors(getInterceptors());  getInterceptors方法会调用子类DelegatingWebMvcConfiguration的addInterceptors方法,接着会调用委托类即我们自定义配置类MyMvcConfigurer类的addInterceptors方法,将自定义的拦截器添加到拦截器注册类中,而后通过拦截器注册类获取到拦截器列表,最后将拦截器添加到映射处理器handlerMapping中,供后续使用。
最后看下请求处理的DispatcherServlet#doDispatch方法 (为了看的更清楚一点删掉了一些代码)
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2.         HttpServletRequest processedRequest = request;
  3.         // 处理程序执行链
  4.         HandlerExecutionChain mappedHandler = null;
  5.         try {
  6.                 ModelAndView mv = null;
  7.                 Exception dispatchException = null;
  8.                 try {
  9.                         // Determine handler for the current request.
  10.                         // 遍历handlerMapping获取能处理request的处理器,mappedHandler里封装着之前我们定义的拦截器供后续调用
  11.                         mappedHandler = getHandler(processedRequest);
  12.                         if (mappedHandler == null) {
  13.                                 noHandlerFound(processedRequest, response);
  14.                                 return;
  15.                         }
  16.                         // Determine handler adapter for the current request.
  17.                         // 确定处理当前请求的处理适配器 RequestMappingHandlerAdapter
  18.                         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  19.                         // 执行handler之前应用拦截器执行拦截器的后置方法 返回为false表示请求不合理直接返回了
  20.                         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  21.                                 return;
  22.                         }
  23.                         // Actually invoke the handler.
  24.                         // 真正执行这个HandlerMethod
  25.                         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  26.             
  27.                         applyDefaultViewName(processedRequest, mv);
  28.                         // 执行拦截器的后置方法
  29.                         mappedHandler.applyPostHandle(processedRequest, response, mv);
  30.                 }
  31.                 catch (Exception ex) {
  32.                         dispatchException = ex;
  33.                 }
  34.                 catch (Throwable err) {
  35.                         // As of 4.3, we're processing Errors thrown from handler methods as well,
  36.                         // making them available for @ExceptionHandler methods and other scenarios.
  37.                         dispatchException = new NestedServletException("Handler dispatch failed", err);
  38.                 }
  39.                 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  40.         }
  41.         catch (Exception ex) {
  42.                 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  43.         }
  44.         catch (Throwable err) {
  45.                 triggerAfterCompletion(processedRequest, response, mappedHandler,
  46.                                 new NestedServletException("Handler processing failed", err));
  47.         }
  48.         finally {
  49.         }
  50. }
  51. #mappedHandler.applyPreHandle
  52. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  53.         HandlerInterceptor[] interceptors = getInterceptors();
  54.         if (!ObjectUtils.isEmpty(interceptors)) {
  55.                 for (int i = 0; i < interceptors.length; i++) {
  56.                         HandlerInterceptor interceptor = interceptors[i];
  57.                         // 前置处理为false时
  58.                         if (!interceptor.preHandle(request, response, this.handler)) {
  59.                                 // 触发拦截器的afterCompletion方法
  60.                                 triggerAfterCompletion(request, response, null);
  61.                                 return false;
  62.                         }
  63.                         this.interceptorIndex = i;
  64.                 }
  65.         }
  66.         return true;
  67. }
复制代码
可以看到再真正执行handler之前会调用mappedHandler.applyPreHandle 方法,遍历拦截器执行preHandle方法,若返回false则根据先前执行过的拦截器顺序倒序执行afterCompletion方法,都通过的话后续执行handler获取请求结果,再接着执行拦截器的postHandle方法最后执行afterCompletion方法。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美丽的神话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表