Spring5源码,Spring Web中的处理程序执行链
Spring的DispatcherServlet假如缺少几个关键元素将无法分派请求,其中最重要的一个是处理程序执行链。
一、什么是Spring中的处理程序执行链?
Spring中的处理程序执行链是一种由处理程序映射和处理程序拦截器(简单点说就是由谁来处理,处理之前和之后应该干点啥)组成的责任链设计模式。处理器映射器用于将当前请求与其专用的controller进行匹配。拦截器是用来在一些调度动作(如controller解析,视图渲染等)之前和之后所调用的对象。
我们所说的一个处理程序执行链是dispatcher servlet用来处理接收到的请求的一组元素。需要说的是,所有执行链调用都由dispatcher servlet类来进行。其实执行链只是一种容器(见源码):
- 定义处理程序映射和拦截器
 - 定义在某些时刻应用所应该调度的方法(如处理程序适配器适配之后,controller的方法调用之后等等)
 
二、HandlerExecutionChain类
处理程序执行链由org.springframework.web.servlet.HandlerExecutionChain类表示。它的主要包含两个私有字段:Object handler 和 HandlerInterceptor[] interceptors,它们被用在请求的调度过程中。第一个包含用于查找处理程序适配器实例的处理程序对象。第二个是包含拦截器的数组,用来应用于处理过的请求(这里这么说是因为这是一条执行链,一个接一个来对这个请求进行处理)。
在DispatcherServlet类中,HandlerExecutionChain的查找通过protected HandlerExecutionChain getHandler(HttpServletRequest request)完成。它遍历所有可用的处理程序映射,并返回能够处理请求的第一个处理程序。
在DispatcherServlet与HandlerExecutionChain实例中要完成的第二件事是应用拦截器的前后调用。这是由DispatcherServlet的方法,如applyPreHandle,applyPostHandle,applyAfterConcurrentHandlingStarted和triggerAfterCompletion。
public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    private final Object handler;
    @Nullable
    private HandlerInterceptor[] interceptors;
    @Nullable
    private List<HandlerInterceptor> interceptorList;
    private int interceptorIndex = -1;
  ...
    /**
     * 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)
            throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }
    /**
     * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
     */
    void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                if (interceptors[i] instanceof AsyncHandlerInterceptor) {
                    try {
                        AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                        asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
                    }
                    catch (Throwable ex) {
                        logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
                    }
                }
            }
        }
    }
 /**
     * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
     */
    void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                if (interceptors[i] instanceof AsyncHandlerInterceptor) {
                    try {
                        AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                        asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
                    }
                    catch (Throwable ex) {
                        logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
                    }
                }
            }
        }
    }
org.springframework.web.servlet.DispatcherServlet
/**
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     */
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping hm : this.handlerMappings) {
                if (logger.isTraceEnabled()) {
                    logger.trace(
                            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
                }
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
...
  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        //HandlerExecutionChain定义出来,做成函数内局部变量可以做到逃逸管理,和request请求做到用完即毁,提        //高性能,防止内存泄漏
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                // Determine handler for the current request.
                //拿到执行链
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                // Determine handler adapter for the current request.
                //通过处理器找到相应的适配器,其实就是个拓展代理,参考之前我写的Spring设计模式
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //提前批预处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                // Actually invoke the handler.
                //执行核心处理
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //默认视图渲染
                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 {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                //如果是异步的话,不需要解释了吧,看上面英文
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
三、自定义处理程序执行链
了说明处理程序执行链的使用,我们将从关于Spring DispatcherServlet生命周期的文章中拿到我们自定义的dispatcher servlet类,并向其添加一个自定义的处理程序执行链。但是,无须深挖并使用HandlerExecutionChain中的所有Spring的类,我们来创建一个新的对象(DumberHandlerExecutionChain),添加两个方法来调用拦截器,并在DispatcherServlet的类中使用它。请看下面编写的代码:
// we start directly by doService method which handles incoming request
// retrieve execution chain and handler adapters adapted to received request
DumberHandlerExecutionChain executionChain = new DumberHandlerExecutionChain(getHandlerExecutionChain(request));
System.out.println("Working with following handler execution chain: "+executionChain);
HandlerAdapter adapter = getHandlerAdapter(executionChain.getHandler());
if (!executionChain.preHandle(request, response)) {
    throw new IllegalStateException("Some of defined interceptors weren't ivoked correctly.");
}
// handle the request and try to generate a ModelAndView instance
ModelAndView modelView = adapter.handle(request, response, executionChain.getHandler());
if (modelView == null) {
    throw new IllegalStateException("Handled ModelAndView can't be null (handled with adapter: "+adapter+")");
}
if (!modelView.isReference()) {
    throw new UnsupportedOperationException("Only view models defined as references can be used in this servlet");
}
executionChain.postHandle(request, response, modelView);
只需要改变3行代码。第一个是DumberHandlerExecutionChain实例的定义,替换掉HandlerExecutionChain。第二个更改是applyPreHandler和applyPostHandler方法。这段代码这样来看的话好理解吧。我们来看看DumberHandlerExecutionChain类的定义:
public class DumberHandlerExecutionChain extends HandlerExecutionChain {
    public DumberHandlerExecutionChain(HandlerExecutionChain chain) {
        super(chain);
        System.out.println("Overriden constructor DumberHandlerExecutionChain invoked");
    }
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for (HandlerInterceptor interceptor : getInterceptors()) {
            System.out.println("Running pre handler for :"+interceptor);
            if (!interceptor.preHandle(request, response, this.getHandler())) {
                System.out.println("An error occured on calling handler for "+interceptor);
                return false;
            }
        }
        return true;
    }
    public void postHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView viewModel) throws Exception {
        for (HandlerInterceptor interceptor : getInterceptors()) {
            interceptor.postHandle(request, response, this.getHandler(), viewModel);
            System.out.println("Running post handler for :"+interceptor);
        }
    }
    @Override
    public String toString() {
        return "DumberHandlerExecutionChain {interceptors :"+Arrays.asList(this.getInterceptors())+", handler: "+this.getHandler()+"}";
    }
}
它们是前面提到的两种方法:preHandle和postHandle。两者很相似。他们首先迭代所有可用的拦截器。区别在于第一个调用拦截器的preHandle方法和第二个拦截器的postHandle方法。第二个区别是结果。如果所有拦截器正确完成了操作,preHandle将返回true。postHandle不返回任何东西(这里和HandlerExecutionChain源码内相应的方法实现大致一样,但是做了逻辑上的简单处理达到咱们想要的效果即可)。
但这两种方法并不是这个类的核心。它最重要的地方是调用这个父类的构造函数:
// 1. Invoked directly by super(chain) call
public HandlerExecutionChain(Object handler) {
        this(handler, null);
}
// 2. Called directly by previous constructor
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList<>();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        }
        else {
            this.handler = handler;
            this.interceptors = interceptors;
        }
    }
由上可以看到,通过handler instanceof检查,我们可以在dispatcher servlet中本地生成HandlerExecutionChain。我们不需要查找产生HandlerExecutionChain实例的处理程序映射(例如:org.springframework.web.servlet.handler.AbstractHandlerMapping或org.springframework.web.servlet.handler.AbstractUrlHandlerMapping的实现类)并覆盖重写现有代码。而不是使用这些复杂的步骤,我们只需简单地将HandlerExecutionChain的实例传递给我们自定义的执行链类的构造函数即可。
完成上面的工作,接下来,你可以在日志中看到以下信息:
Overriden constructor DumberHandlerExecutionChain invoked
//tag1
Working with following handler execution chain: DumberHandlerExecutionChain {interceptors :[org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor@77f6d2e3, com.migo.interceptors.LotteryInterceptor@6d8f729c], handler: public java.lang.String com.migo.controller.TestController.test(javax.servlet.http.HttpServletRequest)}
//pre
Running pre handler for :org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor@77f6d2e3
Running pre handler for :com.migo.interceptors.LotteryInterceptor@6d8f729c
[LotteryInterceptor] preHandle
//excute Controller
Controller asks, are you a lottery winner ? true
Current locale is :org.springframework.web.servlet.DispatcherServlet$1@5cf346dc
Request attributes are :org.apache.catalina.connector.RequestFacade@7d9ccb73
//post
Running post handler for :org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor@77f6d2e3
[LotteryInterceptor] postHandle
Running post handler for :com.migo.interceptors.LotteryInterceptor@6d8f729c
转载:
芋道源码
Spring5源码,Spring Web中的处理程序执行链的更多相关文章
- 5.2 Spring5源码--Spring AOP源码分析二
		
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
 - 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
		
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
 - spring5源码编译过程中必经的坑
		
spring源码编译流程:Spring5 源码下载 第 一 步 : https://github.com/spring-projects/spring-framework/archive/v5.0.2 ...
 - 1. spring5源码 -- Spring整体脉络 IOC加载过程 Bean的生命周期
		
可以学习到什么? 0. spring整体脉络 1. 描述BeanFactory 2. BeanFactory和ApplicationContext的区别 3. 简述SpringIoC的加载过程 4. ...
 - 5.1 Spring5源码--Spring AOP源码分析一
		
目标: 1.什么是AOP, 什么是AspectJ, 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP 1.1 什么是 ...
 - 5.3 Spring5源码--Spring AOP使用接口方式实现
		
Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解. 本文重点介绍Spring使用接口方式实现AOP. 使用接口方式实现AOP以了解为目的. 更好地理解动态 ...
 - 5.2 spring5源码--spring AOP源码分析三---切面源码分析
		
一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...
 - Spring5源码解析-Spring框架中的单例和原型bean
		
Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...
 - 3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终解决方案
		
根据之前解析的循环依赖的源码, 分析了一级缓存,二级缓存,三级缓存的作用以及如何解决循环依赖的. 然而在多线程的情况下, Spring在创建bean的过程中, 可能会读取到不完整的bean. 下面, ...
 
随机推荐
- Pytest测试框架(二):pytest 的setup/teardown方法
			
PyTest支持xUnit style 结构, setup() 和 teardown() 方法用于初始化和清理测试环境,可以保证测试用例的独立性.pytest的setup/teardown方法包括:模 ...
 - CentOS 7 最小化安装及优化
			
CentOS 7 最小化安装及优化 目录 CentOS 7 最小化安装及优化 一.下载镜像文件 官方网站 国内镜像源 一.VMware 配置虚拟网络 二.VMware 新建虚拟机 三.CentOS 7 ...
 - reactor模式:多线程的reactor模式
			
上文说到单线程的reactor模式 reactor模式:单线程的reactor模式 单线程的reactor模式并没有解决IO和CPU处理速度不匹配问题,所以多线程的reactor模式引入线程池的概念, ...
 - ConcurrentHashMap 并发之美
			
一.前言 她如暴风雨中的一叶扁舟,在高并发的大风大浪下疾驰而过,眼看就要被湮灭,却又在绝境中绝处逢生 编写一套即稳定.高效.且支持并发的代码,不说难如登天,却也绝非易事. 一直有小伙伴向我咨询关于Co ...
 - IDEA:配置Tomcat并运行应用
			
1.File->ProjectStructre->Artifacts 如下界面 3.下一步:如图所示 4.选择相应的Module就行 5.第一次运行程序时最好选择运行的配置,否则可能运行的 ...
 - Spring Boot 2.x基础教程:使用Flyway管理数据库版本
			
之前已经介绍了很多在Spring Boot中使用MySQL的案例,包含了Spring Boot最原始的JdbcTemplate.Spring Data JPA以及我们国内最常用的MyBatis.同时, ...
 - 【Java基础】面向对象上
			
面向对象上 这一章主要涉及 Java 类及类的成员,包括属性.方法.构造器:代码块.内部类. 面向过程与面向对象 面向过程(Procedure Oriented Programming,POP)与面向 ...
 - 剑指offer 树的基本操作:四种遍历方式
			
前序遍历 递归版 编程思想 即借助系统栈,效率较低.二叉树的前序遍历规则:1. 访问根结点: 2. 遍历左子树: 3. 遍历右子树 编程实现 //树的定义 struct TreeNode { int ...
 - 【Linux】1、命令行及命令参数
			
命令行及命令参数 文章目录 命令行及命令参数 1.命令行提示符 2.命令和命令参数 简单的命令 date ls 命令参数 短参数(一个字母) 长参数(多个字母) 参数的值 其它参数 3.小结 4.参考 ...
 - 【ORA】ORA-32004: 问题分析和解决
			
今天做一个特殊的实验,需要重启数据库 数据库关闭没有问题 SQL> shutdown immediate; Database closed. Database dismounted. ORACL ...