HandlerInterceptor

114 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

1. HandlerInterceptor

1.1. 拦截器介绍

HandlerInterceptor可以为某些处理器注册任意数量已有或自定义的拦截器,用于添加预处理行为,而无需更改具体处理器实现。

HandlerInterceptor的源码分为两个过程:

  • 拦截器查找过程
  • 拦截器应用过程

1.2. 拦截器源码

HandlerInterceptor就是一个接口,里面的方法就三个方法需要实现。

public interface HandlerInterceptor {
​
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
​
        return true;
    }
​
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
    }
​
​
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
    }
​
}

何时创建

我们在前面的学习中知道了initHandlerMappings的作用,其中就有初始化DispatcherServlet.properties里面的类(bean),以SimpleUrlHandlerMapping为例:

我们前面的这张图不知道大家记不记得

image-20220808095447382

initializeBean后面我们上面进入的是invokeInitMethods方,但这个方法上面有applyBeanPostProcessorsBeforeInitialization这个方法

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    ...
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    ...
}

进入里面

其实就是在初始化之前应用 Bean 后处理器

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
​
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      Object current = processor.postProcessBeforeInitialization(result, beanName); // <- 这里
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

再进入postProcessBeforeInitialization方法,初始化前的后处理

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
         bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
         bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
         bean instanceof ApplicationStartupAware)) {
      return bean;
   }
​
   AccessControlContext acc = null;
​
   if (System.getSecurityManager() != null) {
      acc = this.applicationContext.getBeanFactory().getAccessControlContext();
   }
​
   if (acc != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
         invokeAwareInterfaces(bean);  // <-- 这里
         return null;
      }, acc);
   }
   else {
      invokeAwareInterfaces(bean);  // <-- 这里
   }
​
   return bean;
}

里面有初始化一些接口,其他的我们不看,就看最后一条接口setApplicationContext

private void invokeAwareInterfaces(Object bean) {
   ....
   if (bean instanceof ApplicationContextAware) {
      ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
   }
}

ApplicationObjectSupport实现了这个接口,首先调用setApplicationContext()方法,其中的initApplicationContext()方法由子类覆盖和实现。

protected void initApplicationContext(ApplicationContext context) throws BeansException {
   initApplicationContext();
}

通过覆盖initApplicationContext()方法实现初始化的一些工作。

在子类SimpleUrlHandlerMapping中的initApplicationContext()方法中,先初始化Spring MVC容器,然后再对Handler进行注册。

public void initApplicationContext() throws BeansException {
   super.initApplicationContext(); // <- 这里
   registerHandlers(this.urlMap);
}

调用父类的初始化程序

protected void initApplicationContext() throws BeansException {
   extendInterceptors(this.interceptors);  // 里面是空方法 给定已配置的拦截器,子类可以覆盖以注册其他拦截器的扩展挂钩 
   detectMappedInterceptors(this.adaptedInterceptors);
   initInterceptors();
}

我们看到了这个父方法就是用来初始化拦截器的,英文注释写的也很清楚Initializes the interceptors.

detectMappingInterceptors()方法探测ApplicationContext中已经解析过的MappedInterceptorinitInterceptors()方法来初始化拦截器。

protected void initInterceptors() {
   if (!this.interceptors.isEmpty()) {
      for (int i = 0; i < this.interceptors.size(); i++) {
         Object interceptor = this.interceptors.get(i);
         if (interceptor == null) {
            throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
         }
         this.adaptedInterceptors.add(adaptInterceptor(interceptor));
      }
   }
}

调用initInterceptors()方法将SimpleUrlHandlerMapping中定义的interceptors包装成HandlerInterceptor对象保存在adaptedInterceptors数组中。

再回到initApplicationContext()方法中调用的registerHandlers()方法,这边主要是主要是对urlMap中的key值进行了一些处理,要是没有“/”的就加上"/",去掉空格等处理。这里的urlMap就是在配置文件中SimpleUrlHandlerMapping的通过mappings属性注入的的内容。key是url的某个字段,value是bean的id。j

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
   if (urlMap.isEmpty()) {
      logger.trace("No patterns in " + formatMappingName());
   }
   else {
      urlMap.forEach((url, handler) -> {
         // Prepend with slash if not already present.
         if (!url.startsWith("/")) {
            url = "/" + url;
         }
         // Remove whitespace from handler bean name.
         if (handler instanceof String) {
            handler = ((String) handler).trim();
         }
         registerHandler(url, handler);
      });
      logMappings();
   }
}

从这边就可以清楚的看到,这里根据SimpleUrlHandlerMapping中的urlMap中的value值通过getBean()方法得到bean对象(通过id查找)。同时将url的某个字段作为key值,bean作为value重新存入到AbstractUrlHandlerMapping的urlMap属性中去,这样就达到url的某个字段对应到具体的controller了的目的,当遇到有请求访问服务器的时候,就可以根据url找到具体的controller去执行这个请求了。

img

调用顺序

我们写个测试用例来了解一下执行顺序

@Component
public class MyInterceptor implements HandlerInterceptor {
​
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("===========preHandle===========");
        return true;
    }
​
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("===========postHandle===========");
​
    }
​
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("===========afterCompletion===========");
​
    }
}

controller

@GetMapping("/hello")
public String hello() {
    System.out.println("controller");
    return "ok";
}

我们执行一下/hello的请求,看看结果

image-20220807225056139

一共4个断点,每个方法结束就会输出对应的语句,可以看到,其实我们的handle就已经是去执行了我们controller的方法了

进入第一个断点的applyPreHandle

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;
}

遍历我们定义的拦截器,只要最前面的拦截了就返回false,因为this.interceptorList是个List类型的,所以存放的顺序是定义的顺序,先定义的就先进行判断。

再来到applyPostHandle方法下

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);
   }
}

可以很清楚的看到for循环是从后往前遍历的,所以他的执行顺序是定义的逆序,并且不需要返回的!!

再来到processDispatchResult方法,前面在这里不重要,我们省略

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
            @Nullable Exception exception) throws Exception {
        ...
            
        if (mappedHandler != null) {
            // Exception (if any) is already handled..
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

triggerAfterCompletion方法里面

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);
      }
   }
}

同样是逆序判断,不需要返回

总结

preHandlepostHandleafterCompletion
调用时间Controller方法处理之前Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作DispatcherServlet进行视图的渲染之后
执行顺序链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行,若返回false,则中断执行链式Intercepter情况下,Intercepter按照声明的顺序倒着执行。逆序执行,可以用来处理异常,清理资源