• SpringBoot源码解读与原理分析(三十七)SpringBoot整合WebMvc(二)DispatcherServlet的工作全流程


    前言

    WebMvc的核心组件装配完成之后,DispatcherServlet作为WebMvc的核心前端控制器正式投入工作,默认接收客户端的所有请求,并调度其它核心组件处理请求,最终响应结果给客户端。

    本节内容研究WebMvc在实际运行期间DispatcherServlet对于请求处理和响应结果的全流程执行原理。本文内容由于不可割裂,因此具有超长预警。

    本文沿用 SpringBoot源码解读与原理分析(三十六)SpringBoot整合WebMvc(一)@Controller控制器装配原理 12.1 SpringBoot整合WebMvc案例 中编写好的示例项目,并进行一些小改动。

    • 新增CustomAdvice类,标注@ControllerAdvice注解,并编写两个标注@InitBinder和@ExceptionHandler的方法
    @ControllerAdvice
    public class CustomAdvice {
    
        @InitBinder
        public void customDataBinder(WebDataBinder dataBinder) {
            // 请求URL中的时间格式为yyyy-MM-dd HH:mm:ss
            // 自动绑定到Date属性参数中
            dataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
        }
    
        @ExceptionHandler({Exception.class})
        public String customExceptionHandler(Exception ex) {
            System.out.println("自定义异常发生了," + ex.getMessage());
            return "自定义异常返回";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 修改UserController类,新增标注@InitBinder注解的方法,test方法新增一个Date类型的参数
    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @InitBinder
        public void myDataBinder(WebDataBinder dataBinder) {
            // 如果参数是字符串类型,则去除字符串的前后空格
            dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
        }
    
        @RequestMapping(method = RequestMethod.GET, value = "/test")
        @ResponseBody
        public String test(String name, Date time) {
            System.out.println("请求参数 name=" + name);
            System.out.println("请求参数 time=" + time);
            return name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 新增拦截器CustomInterceptor类
    public class CustomInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器的preHandle方法执行了...");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器的postHandle方法执行了...");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器的afterCompletion方法执行了...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 修改配置类,加载拦截器及配置其路径
    @Configuration
    public class PathConfig implements WebMvcConfigurer {
    
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.addPathPrefix("/api", c -> c.isAnnotationPresent(Controller.class));
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/api/user/test");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    至此,示例项目代码编写完成,执行主启动类的main方法启动项目。使用浏览器访问 http://127.0.0.1:8080/api/user/test?name=aaa&time=2024-02-29 12:12:12,控制台打印结果如下:

    拦截器的preHandle方法执行了...
    请求参数 name=aaa
    请求参数 time=Thu Feb 29 12:12:12 CST 2024
    拦截器的postHandle方法执行了...
    拦截器的afterCompletion方法执行了...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    12.4 DispatcherServlet的工作全流程

    启动示例项目后,在DispatcherServlet的父类FrameworkServlet的service方法上打入断点,随后使用浏览器发起请求,待程序停在断点处,开始Debug调试。

    12.4.1 DispatcherServlet#service

    源码1FrameworkServlet.java
    
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            // 对PATCH类型的请求单独处理
            processRequest(request, response);
        } else {
            // 调用父类的HttpServlet的service方法
            super.service(request, response);
        }
    }
    
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    
    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    由 源码1 可知,service方法会对PATCH类型的请求单独处理,但通常在项目开发中不会使用PATCH类型

    继续向下执行else块的super.service方法,FrameworkServlet的父类HttpServlet会根据不同的请求类型将方法转发至doXxx方法中,所以最终执行的是FrameworkServlet中重写的doGetdoPostdoPutdoDelete等方法,而这些方法最终都会调用processRequest方法。

    12.4.2 processRequest

    源码2FrameworkServlet.java
    
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 记录接收请求的时间
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        // 获取当前线程的LocaleContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        // 创建当前线程的LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);
        // 获取当前线程的RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 创建当前线程的RequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
    
        // 初始化ContextHolder,传入新封装好LocaleContext和RequestAttributes
        initContextHolders(request, localeContext, requestAttributes);
    
        try {
            // 真正处理请求的方法,但这是子类的模板方法
            doService(request, response);
        } // catch ...
        finally {
            // 重新设置当前线程的LocaleContext和RequestAttributes
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            logResult(request, response, failureCause, asyncManager);
            // 发布ServletRequestHandledEvent事件
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }
    
    private void initContextHolders(HttpServletRequest request,
                                    @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
        // 将全新的LocaleContext和RequestAttributes设置到当前线程中
        if (localeContext != null) {
            LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
        }
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        }
    }
    
    private void resetContextHolders(HttpServletRequest request,
                                     @Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) {
        // 将预先保存的LocaleContext和RequestAttributes设置回线程中
        LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
        RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    由 源码2 可知,processRequest方法会对请求做一些前置处理,再转调doService方法真正处理请求。

    在该方法的前置处理中,做了线程之间的隔离。首先获取当前线程的LocaleContext和RequestAttributes并暂存在方法中,随后创建全新的LocaleContext和RequestAttributes,调用initContextHolders方法设置到当前线程中,以此完成线程之间的隔离。

    待请求完成处理后,在finally代码块中,调用resetContextHolders方法将预先保存好的LocaleContext和RequestAttributes设置回线程中,以恢复原来的线程。

    12.4.3 doService

    源码3DispatcherServlet.java
    
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);
        
        Map<String, Object> attributesSnapshot = null;
        // 判断请求是否由标签而来
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }
    
        // ......
    
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }
    
        try {
            // 真正处理请求的方法
            doDispatch(request, response);
        } // finally ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    由 源码3 可知,doService方法也是在进行一些前置处理之后,转调doDispatch方法真正处理请求。

    12.4.3.1 isIncludeRequest的判断
    源码4WebUtils.java
    
    public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
    public static boolean isIncludeRequest(ServletRequest request) {
        // 判断当前请求中是否包含名为“javax.servlet.include.request_uri”的属性
        return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由 源码4 可知,doService方法的第一个if判断结构会调用WebUtils.isIncludeRequest(request)方法。该方法会判断当前请求中是否包含名为“javax.servlet.include.request_uri”的属性。

    在JSP中,使用 标签可以组合其他JSP页面,那么这个被组合的JSP页面的加载请求就会带上“javax.servlet.include.request_uri”属性。因此,isIncludeRequest方法的作用是区别页面的加载是否由标签而来。

    12.4.3.2 FlashMapManager的设计

    在用户登录的业务场景中,如果是前后端不分离的情况,通常是使用POST请求将用户名、密码等信息传入后端以供认证,认证成功后使用重定向将客户端引导至系统主页。

    在这个前提下有一个特殊的场景:如果用于登录时提交的登录表单中,有一些需要在跳转至主页时渲染的数据,则仅放入request域中无法解决问题。

    Spring引入FlashMapManager来解决这个问题,可以在页面重定向发生跳转时,将需要渲染的数据暂时放入session中,这样浏览器即便刷新也不会影响数据渲染。

    12.4.4 doDispatch

    该方法是处理请求和响应的核心方法,由于篇幅很长,下面拆解来看。

    12.4.4.1 处理文件上传请求
    源码5DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = 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);
    
                // ......
    }
    
    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
                    // logger ...
                }
            } else if (hasMultipartException(request)) {
                // logger ...
            } else {
                try {
                    return this.multipartResolver.resolveMultipart(request);
                } // catch ...
            }
        }
        return request;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    源码6StandardServletMultipartResolver.java
    
    @Override
    public boolean isMultipart(HttpServletRequest request) {
        return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由 源码5 可知,doDispatch方法的第一部分逻辑是处理带有文件上传的请求,其核心组件是MultipartResolver。首先判断请求是否是multipart请求,如果是,则将本由Servlet容器处理的HttpServletRequest对象转换为可以访问请求中的文件对象的MultipartHttpServletRequest子接口对象。

    由 源码6 可知,判断请求是否是multipart请求的方法是判断请求头content-type是否以"multipart/"开头。

    由于当前Debug的请求只是一个普通的GET请求,所以不会进入该部分代码。

    12.4.4.2 获取可用的Handler
    源码7DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 处理文件上传的请求 ...
        
        // 获取可用的Handler
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }
        
        // ......
    }
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    由 源码7 可知,doDispatch方法的第二部分逻辑是获取可用的Handler。在getHandler方法中,遍历所有的HandlerMapping,从中找出可以返回非空HandlerExecutionChain对象的HandlerMapping。

    HandlerMapping集合
    Debug至getHandler方法的内部,可以发现有5个HandlerMapping可以选择。由于实例项目编写的Handler是以@Controller+@RequestMapping注解实现的,因此最终选择出来的HandlerMapping是与之相匹配的RequestMappingHandlerMapping。

    (1)HandlerMapping.getHandler
    源码8AbstractHandlerMapping.java
    
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 留给子类实现的模板方法
        Object handler = getHandlerInternal(request);
        
        // ......
        
        // 构建HandlerExecutionChain对象
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        
        // ......
        return executionChain;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    由 源码8 可知,HandlerMapping的getHandler的方法的核心逻辑有两步:由子类具体获取Handler的具体对象;根据Handler对象构建HandlerExecutionChain对象。

    (2)getHandlerInternal

    Debug进入getHandlerInternal方法,发现程序会进入RequestMappingHandlerMapping的直接父类RequestMappingInfoHandlerMapping中。

    源码9RequestMappingInfoHandlerMapping.java
    
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        try {
            return super.getHandlerInternal(request);
        } finally {
            ProducesRequestCondition.clearMediaTypesAttribute(request);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    由 源码9 可知,RequestMappingInfoHandlerMapping中的getHandlerInternal也是直接使用super.getHandlerInternal方法调用父类AbstractHandlerMethodMapping中的getHandlerInternal方法。

    源码10AbstractHandlerMethodMapping.java
    
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 获取本次请求的URI,并设置到request域中
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        this.mappingRegistry.acquireReadLock();
        try {
            // 获取可以处理当前URI请求的HandlerMethod对象
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            // 创建全新的HandlerMethod对象
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    由 源码10 可知,getHandlerInternal方法首先会借助UrlPathHelper获取本次请求URI(示例项目是"/api/user/test"),并设置到request域中。随后调用lookupHandlerMethod方法,获取可以处理当前URI请求的HandlerMethod对象(实例项目是UserController的test方法)。

    请求URI和HandlerMethod对象
    注意,获取到HandlerMethod对象之后,还要调用createWithResolvedBean方法创建一个全新的HandlerMethod对象。为什么还要再次创建呢?

    注意观察上图,发现HandlerMethod对象中的bean属性是一个字符串"userController",而不是BeanFactory中真实存在的UserController对象,其他属性也没有UserController对象的持有。

    源码11HandlerMethod.java
    
    public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
            String beanName = (String) this.bean;
            // 从BeanFactory中取出bean对象
            handler = this.beanFactory.getBean(beanName);
        }
        // 重新封装HandlerMethod
        return new HandlerMethod(this, handler);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由 源码11 可知,createWithResolvedBean方法的作用就是从BeanFactory中取出UserController对象并重新封装。

    (3)getHandlerExecutionChain

    回到 源码8 ,getHandlerInternal执行完后获得HandlerMethod对象,然后将该HandlerMethod对象传入getHandlerExecutionChain方法,组合跟该Handler相关的拦截器,并封装为执行链HandlerExecutionChain对象。

    源码12AbstractHandlerMapping.java
    
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        // 构造HandlerExecutionChain对象
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        // 获取请求路径
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                // 匹配路径的拦截器处理
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                // 普通拦截器直接添加
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    由 源码12 可知,getHandlerExecutionChain会根据HandlerInterceptor的类型分别进行处理,最终构造一个HandlerExecutionChain对象。

    getHandlerExecutionChain方法
    由上图可知,对示例项目来说,构造的HandlerExecutionChain对象组合了三个拦截器,分别是ConversionServiceExposingInterceptor、ResourceUrlProviderExposingInterceptor,以及示例项目自定义的CustomInterceptor

    获得HandlerMethod对象并组合拦截器封装成HandlerExecutionChain对象之后,HandlerMapping的工作全部完成,接下来回到DispatcherServlet的doDispatch方法。

    12.4.4.3 获取HandlerAdapter
    源码13DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                // 处理文件上传请求
                // 获取可用的Handler
                
                // 获取HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                // ......
    }
    
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
        // throw ex ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    由 源码13 可知,doDispatch方法的第三部分逻辑是获取可以处理当前请求的HandlerAdapter,匹配规则是通过HandlerAdapter的supports方法。

    Debug至getHandlerAdapter方法,发现有4个可选的HandlerAdapter实现类对象:

    getHandlerAdapter方法
    由上图可知,示例项目选择的HandlerAdapter实现类对象是RequestMappingHandlerAdapter。

    源码14AbstractHandlerMethodAdapter.java
    
    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    源码15RequestMappingHandlerAdapter.java
    
    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由 源码14-15 可知,supports方法仅是判断Handler类是否是HandlerMethod对象,supportsInternal方法默认返回true,此处supports方法一定返回true,因此最终会选择可选HandlerAdapter对象集合中下标为0的对象,即RequestMappingHandlerAdapter。

    12.4.4.4 回调拦截器
    源码16DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                // 处理文件上传请求
                // 获取可用的Handler
                // 获取HandlerAdapter
                
                // 回调拦截器
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                
                // ......
    }
    
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取HandlerExecutionChain对象中组合的拦截器
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                // 遍历拦截器,执行preHandle方法
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    由 源码16 可知,doDispatch方法的第四部分逻辑是回调HandlerExecutionChain对象中组合的拦截器,首先会获取HandlerExecutionChain对象中组合的拦截器,然后遍历这些拦截器,执行其preHandle方法。

    注意,每个拦截器preHandle方法如果有一个返回false,则applyPreHandle方法直接返回false,doDispatch方法直接结束不再执行下面的逻辑。

    由 12.4.4.2 节的getHandlerExecutionChain方法分析可知,HandlerExecutionChain对象中组合的拦截器有3个,分别是ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor,以及示例项目自定义的CustomInterceptor。

    源码17ResourceUrlProviderExposingInterceptor.java
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        try {
            request.setAttribute(RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
        } // catch ...
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    源码18ConversionServiceExposingInterceptor.java
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws ServletException, IOException {
        request.setAttribute(ConversionService.class.getName(), this.conversionService);
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    源码19CustomInterceptor.java
    
    public class CustomInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器的preHandle方法执行了...");
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    有 源码17-19 以及CustomInterceptor类可知,四两个拦截器的preHandle方法一定会返回true,因此applyPreHandle方法返回true,doDispatch方法继续执行下面的逻辑。

    12.4.4.5 执行Handler
    源码20DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                // 处理文件上传请求
                // 获取可用的Handler
                // 获取HandlerAdapter
                // 回调拦截器
                
                // 实际执行Handler
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                
                // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    由 源码20 可知,doDispatch方法的第五部分逻辑是调用HandlerAdapter的```handle``方法实际执行Handler,返回一个ModelAndView。

    借助IDE,可以发现handle方法的实现在DispatcherServlet的父类AbstractHandlerMethodAdapter中,其handle方法又会转调子类RequestMappingHandlerAdapter的handleInternal方法。

    (1)handleInternal
    源码21AbstractHandlerMethodAdapter.java
    
    @Override
    @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    源码22RequestMappingHandlerAdapter.java
    
    private boolean synchronizeOnSession = false;
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ModelAndView mav;
        checkRequest(request);
    
        // 同步session的配置,默认为fasle
        if (this.synchronizeOnSession) {
            // ......
        } else {
            // 执行Handler方法
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    
        // ......
    
        return mav;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    由 源码21-22 可知,handleInternal方法最核心的逻辑是else代码块中的invokeHandlerMethod方法。

    (2)invokeHandlerMethod

    由于invokeHandlerMethod方法的篇幅很长,下面拆解来看。

    (a)初始化参数绑定器

    在WebMvc中,有两个和参数绑定相关的注解:@InitBinder和@ControllerAdvice。

    @InitBinder注解可以单独声明在一个Controller类中,执行当前Controller类中的方法时,会先执行标注了@InitBinder注解的方法,初始化一些参数绑定器的逻辑。

    @ControllerAdvice注解可以配合@InitBinder注解标注的方法,实现全局的参数绑定器预初始化。

    源码23RequestMappingHandlerAdapter.java
    
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 初始化参数绑定器
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            
            // ......
    }
    
    public static final MethodFilter INIT_BINDER_METHODS = method ->
                AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
    private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
        Class<?> handlerType = handlerMethod.getBeanType();
        Set<Method> methods = this.initBinderCache.get(handlerType);
        if (methods == null) {
            // 获取当前Controller中全部标注了@InitBinder注解的方法,并放到initBinderCache缓存中
            methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
            this.initBinderCache.put(handlerType, methods);
        }
        List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
        // 遍历标注了@ControllerAdvice注解的类
        this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
            if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
                Object bean = controllerAdviceBean.resolveBean();
                // 遍历这些类中的全部标注了@InitBinder注解的方法,添加到initBinderMethods缓存中
                for (Method method : methodSet) {
                    initBinderMethods.add(createInitBinderMethod(bean, method));
                }
            }
        });
        // 遍历当前Controller中全部标注了@InitBinder注解的方法
        // 将这些方法添加到initBinderMethods缓存中
        // 相当于合并了Controller类中和标注了@ControllerAdvice注解的类中的方法
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            initBinderMethods.add(createInitBinderMethod(bean, method));
        }
        // 创建数据绑定工厂
        return createDataBinderFactory(initBinderMethods);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    底层收集和执行@InitBinder注解的方法就是invokeHandlerMethod方法中的getDataBinderFactory方法。

    由 源码23 可知,getDataBinderFactory方法会分别收集Controller类中标注了@InitBinder注解的方法、标注了@ControllerAdvice注解的类中标注了@InitBinder注解的方法,并合并起来共同组成参数绑定器。

    示例项目中编写的两类绑定器都被收集起来了,如图:

    自定义的参数绑定器
    在UserController中定义的参数绑定器是:

    @InitBinder
    public void myDataBinder(WebDataBinder dataBinder) {
        // 如果参数是字符串类型,则去除字符串的前后空格
        dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果暂时把@InitBinder注解注释掉,该绑定器不再生效,执行结果如下:

    没有@InitBinder时有空格
    如果添加了把@InitBinder注解注解,该绑定器生效,执行结果如下:

    有@InitBinder时无空格

    CustomAdvice类中定义的参数绑定器是:

    @ControllerAdvice
    public class CustomAdvice {
    
        @InitBinder
        public void customDataBinder(WebDataBinder dataBinder) {
            // 请求URL中的时间格式为yyyy-MM-dd HH:mm:ss
            // 自动绑定到Date属性参数中
            dataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果时间类型的参数不按照 yyyy-MM-dd HH:mm:ss 的格式传递,在浏览器访问:http://127.0.0.1:8080/api/user/test?name=aaa&time=2024年2月29日 12时12分12秒,结果如下:

    IllegalArgumentException: Parse attempt failed for value [2024年2月29日 12时12分12秒]
    
    • 1

    如果时间类型的参数按照 yyyy-MM-dd HH:mm:ss 的格式传递,在浏览器访问:http://127.0.0.1:8080/api/user/test?name=aaa&time=2024-02-29 12-12-12,则正常访问。

    通过以上测试,发现自定义的参数绑定器均生效了。

    (b)参数预绑定
    源码24RequestMappingHandlerAdapter.java
    
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 初始化参数绑定器 ...
            
            // 参数预绑定
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
            
            // ......
    }
    
    public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
    			(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
    					AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
    private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
        SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
        Class<?> handlerType = handlerMethod.getBeanType();
        Set<Method> methods = this.modelAttributeCache.get(handlerType);
        if (methods == null) {
            // 获取当前Controller中全部标注了@ModelAttribute注解且没有被@RequestMapping注解标注的方法
            // 并放到modelAttributeCache缓存中
            methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
            this.modelAttributeCache.put(handlerType, methods);
        }
        List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
        // 遍历标注了@ControllerAdvice注解的类
        this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
            if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
                Object bean = controllerAdviceBean.resolveBean();
                // 遍历这些类中的全部标注了@ModelAttribute注解的方法,添加到attrMethods缓存中
                for (Method method : methodSet) {
                    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
                }
            }
        });
        // 遍历当前Controller中全部标注了@ModelAttribute注解的方法
        // 将这些方法添加到attrMethods缓存中
        // 相当于合并了Controller类中和标注了@ControllerAdvice注解的类中的方法
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
        }
        return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    由 源码24 可知,getModelFactory方法和上一步的getDataBinderFactory方法的逻辑结构几乎一样,都是将Controller类中的标注了某个注解的方法,与全局类中标注了某个注解的方法合并起来。

    不同的是,getDataBinderFactory方法找的是@InitBinder注解,而getModelFactory方法找的是@ModelAttribute注解;相同的是,全局类都标注@ControllerAdvice注解。

    (c)创建方法执行对象
    源码25RequestMappingHandlerAdapter.java
    
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 初始化参数绑定器 ...
            // 参数预绑定 ...
            
            // 创建方法执行对象
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            
            // ......
    }
    
    protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
        return new ServletInvocableHandlerMethod(handlerMethod);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    由 源码25 可知,createInvocableHandlerMethod方法只是将HandlerMethod对象二度封装为ServletInvocableHandlerMethod对象。

    (d)执行Controller的方法invokeAndHandle
    源码26RequestMappingHandlerAdapter.java
    
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 初始化参数绑定器 ...
            // 参数预绑定 ...
            // 创建方法执行对象 ...
            // 创建ModelAndView的容器ModelAndViewContainer ...(不重要)
            // 对异步请求的支持 ...(不重要)
            
            // 执行Controller的方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            // 包装ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        } // finally ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    由 源码26 可知,创建方法执行对象ServletInvocableHandlerMethod后,又创建了ModelAndView的容器ModelAndViewContainer,以及处理了对异步请求的支持,最后调用方法执行对象ServletInvocableHandlerMethod的invokeAndHandle方法执行Handler方法。

    由于invokeAndHandle方法的篇幅很长,下面拆解来看。

    • (i)反射执行Controller方法
    源码27ServletInvocableHandlerMethod.java
    
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
        // 反射执行Controller方法
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        
        // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    源码28InvocableHandlerMethod.java
    
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {
        // 获取参数值列表
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        // logger ...
        return doInvoke(args);
    }
    
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            // 利用反射机制执行Handler
            return getBridgedMethod().invoke(getBean(), args);
        }
        // catch ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    由 源码27-28 可知,invokeAndHandle方法调用invokeForRequest方法,会先收集好执行Handler方法所需的参数列表,然后利用反射机制执行目标Handler方法。doInvoke方法执行完毕后,意味着项目中编写的Controller类的方法已经执行完毕。

    • (ii)处理方法返回值
    源码29ServletInvocableHandlerMethod.java
    
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
        // 反射执行Controller方法
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        
        // ......
        
        // 处理方法返回值
        try {
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } // catch ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    源码30HandlerMethodReturnValueHandlerComposite.java
    
    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 筛选出可以处理当前Controller返回的HandlerMethodReturnValueHandler对象
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            // throw ......
        }
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
    
    private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
        boolean isAsyncValue = isAsyncReturnValue(value, returnType);
        // 遍历HandlerMethodReturnValueHandler集合
        for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
            if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                continue;
            }
            // 找出支持当前返回值类型的
            if (handler.supportsReturnType(returnType)) {
                return handler;
            }
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    由 源码29-30 可知,处理方法返回值的handleReturnValue方法会先筛选出可以处理当前Controller返回的HandlerMethodReturnValueHandler对象,再调用其handleReturnValue方法处理返回值。

    筛选HandlerMethodReturnValueHandler对象的selectHandler方法,是利用for循环遍历HandlerMethodReturnValueHandler对象集合,找出其中支持当前返回值类型的HandlerMethodReturnValueHandler对象。

    借助Debug工具,可以发现可选的HandlerMethodReturnValueHandler对象有15个,示例项目选择的是RequestResponseBodyMethodProcessor。

    筛选HandlerMethodReturnValueHandler对象
    在示例项目中,UserController的test方法上标注了@ResponseBody注解,因此响应的是JSON数据。处理JSON数据响应的底层实现就是RequestResponseBodyMethodProcessor。

    源码31RequestResponseBodyMethodProcessor.java
    
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由 源码31 可知,RequestResponseBodyMethodProcessor中supportsReturnType方法就是判断方法所在类或者方法上是否标注@ResponseBody注解。再一次印证RequestResponseBodyMethodProcessor是处理JSON数据响应的底层实现。

    • (iii)处理JSON数据响应

    获取RequestResponseBodyMethodProcessor对象后,调用其handleReturnValue方法正式处理返回值。

    源码32RequestResponseBodyMethodProcessor.java
    
    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
        // 使用JSON序列化的方式将方法返回的数据转化为文本
        // 并写入HttpServletResponse的输出流中
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    由 源码32 可知,handleReturnValue方法会执行writeWithMessageConverters方法,使用JSON序列化的方式将方法返回的数据转化为文本,并写入HttpServletResponse的输出流中。

    • (iiii)处理视图返回

    示例项目中是响应JSON数据,但在实际开发中也有可能是响应视图。对于返回视图和返回JSON数据,底层使用的HandlerMethodReturnValueHandler并不相同。

    如果一个Controller方法要跳转视图,则方法的返回值一定是一个字符串,并且方法和类上都没有标注@ResponseBody注解,这种情况下底层使用的HandlerMethodReturnValueHandler是ViewNameMethodReturnValueHandler。

    源码33ViewNameMethodReturnValueHandler.java
    
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        Class<?> paramType = returnType.getParameterType();
        // 判断返回值类型是否是CharSequence
        return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
    }
    
    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    
        if (returnValue instanceof CharSequence) {
            String viewName = returnValue.toString();
            // 将String类型的返回值作为逻辑视图名放入ModelAndView容器中
            mavContainer.setViewName(viewName);
            if (isRedirectViewName(viewName)) {
                mavContainer.setRedirectModelScenario(true);
            }
        } else if (returnValue != null) {
            // should not happen
            // throw ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    由 源码33 可知,处理视图响应的handleReturnValue方法会将String类型的返回值作为逻辑视图名称,并放入ModelAndView容器ModelAndViewContainer对象中。

    至此,invokeAndHandle方法执行完毕,Controller类中的方法已执行,ModelAndViewContainer中已经封装了响应视图名称,或者将需要响应的数据写入了HttpServletResponse中。

    (e)包装ModelAndView
    源码34RequestMappingHandlerAdapter.java
    
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 初始化参数绑定器 ...
            // 参数预绑定 ...
            // 创建方法执行对象 ...
            // 创建ModelAndView的容器ModelAndViewContainer ...(不重要)
            // 对异步请求的支持 ...(不重要)
            // 执行Controller的方法 ...
            
            // 包装ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        } // finally ...
    }
    
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                         ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
        // ......
        
        // 取出容器中的ModelMap
        ModelMap model = mavContainer.getModel();
        // 取出视图名称,构造ModelAndView
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        // ......
        return mav;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    由 源码34 可知,invokeHandlerMethod方法的最后一步是调用getModelAndView方法包装ModelAndView对象。在该方法在中会从上一步封装的ModelAndViewContainer对象中取出ModelMap和视图名称,构造成ModelAndView对象并返回。

    简言之,getModelAndView方法完成了ModelAndViewContainer对象到ModelAndView对象的转换。

    至此,invokeHandlerMethod方法执行完毕,HandlerAdapter的工作全部完成,流程回到DispatcherServlet的doDispatch方法。

    12.4.4.6 再次回调拦截器
    源码35DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                // 处理文件上传请求 ...
                // 获取可用的Handler ...
                // 获取HandlerAdapter ...
                // 回调拦截器 ...
                // 执行Handler ...
                
                // 再次回调拦截器
                mappedHandler.applyPostHandle(processedRequest, response, mv);
                // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    源码36HandlerExecutionChain.java
    
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
            throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            // 此处是倒序回调
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由 源码35-36 可知,再次回调拦截器的applyPostHandle方法和 12.4.4.4 节的applyPreHandle的逻辑是几乎一样,不一样的是,这次回调拦截器的顺序是反过来的,调用的是拦截器的postHandle方法。

    12.4.4.7 处理视图、解析异常
    源码37DispatcherServlet.java
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                // 处理文件上传请求 ...
                // 获取可用的Handler ...
                // 获取HandlerAdapter ...
                // 回调拦截器 ...
                // 执行Handler ...
                // 再次回调拦截器 ...
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } // catch ...
            // 处理视图、解析异常
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            
            // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    由 源码37 可知,DispatcherServlet的doDispatch方法的最后一个关键步骤是processDispatchResult方法,该方法会进行视图处理,以及解析整个请求处理中抛出的异常。

    该方法分为三个步骤,下面拆解来看。

    (1)处理异常
    源码38DispatcherServlet.java
    
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
    
        boolean errorView = false;
        // 处理异常
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                // logger ...
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            } else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }
    
        // ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    由 源码38 可知,processDispatchResult会根据异常的类型做不同的处理。

    DispatcherServlet在处理客户端发起的请求时,中间调用Controller或者Service等组件时抛出的异常都几乎不可能是ModelAndViewDefiningException(前面的源码分析也没有见过这个异常),因此if代码块不重要。

    在else代码块中,会调用processHandlerException方法,该方法才是处理异常的核心方法。

    源码39DispatcherServlet.java
    
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                                   @Nullable Object handler, Exception ex) throws Exception {
    
        // ......
        
        // 从HandlerExceptionResolver集合中找到可以处理当前异常的
        // 构造成ModelAndView对象并返回
        ModelAndView exMv = null;
        if (this.handlerExceptionResolvers != null) {
            for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
                exMv = resolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
        }
        // ......
    
        throw ex;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    由 源码39 可知,processHandlerException方法会从HandlerExceptionResolver集合中找到可以处理当前异常的,构造成ModelAndView对象并返回。

    在示例项目中,CustomAdvice类标注了@ControllerAdvice注解,并且声明了标注@ExceptionHandler注解的方法,可实现全局统一异常处理。

    @ControllerAdvice
    public class CustomAdvice {
    
        @ExceptionHandler({Exception.class})
        public String customExceptionHandler(Exception ex) {
            System.out.println("自定义异常发生了," + ex.getMessage());
            return "自定义异常返回";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在UserController的test方法加一行代码:int i = 1/0,发起请求时一定会抛出异常,控制台的结果如下:

    自定义异常发生了,/ by zero
    
    • 1

    可见,全局统一异常处理生效了。

    (2)渲染视图
    源码40DispatcherServlet.java
    
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        // 处理异常 ...
        
        // 渲染视图
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
    
        // ......
    }
    
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 国际化处理 ...
    
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // 根据视图名解析出视图
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                // throw ...
            }
        } else {
            // 否则,直接获取视图;如果还没有视图,则抛出异常
            view = mv.getView();
            if (view == null) {
                // throw ...
            }
        }
    
        // logger ...
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            // 渲染视图
            view.render(mv.getModelInternal(), request, response);
        } // catch ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    由 源码40 可知,无论是正常响应还是抛出异常,最终都会生成一个ModelAndView对象,紧接着就要进行视图的渲染,而渲染视图的核心方法是render方法。

    渲染视图的render方法会从ModelAndView中获取逻辑视图的名称,如果有名称则借助ViewResolver去匹配视图,如果成功匹配到则返回,如果匹配不到则抛出异常。匹配生成视图View对象后,执行view.render方法实际渲染视图。

    (3)第三次回调拦截器
    源码41DispatcherServlet.java
    
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        // 处理异常 ...
        // 渲染视图 ...
        
        // 第三次回调拦截器
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    源码42HandlerExecutionChain.java
    
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
            throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            // 遍历拦截器,执行其afterCompletion方法
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } // catch ...
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    由 源码41-42 可知,视图渲染完成后,最后的收尾动作是回调所有拦截器的afterCompletion方法。

    这已经是第三次回调拦截器了,第一次执行拦截器的preHandle方法,第二次倒序执行拦截器的postHandle方法,第三次执行拦截器的afterCompletion方法。其中,第一次是在Handler方法执行之前执行的,第二次和第三次是在之后执行的。

    拦截器执行流程
    因此,控制台的输出如下:

    拦截器的preHandle方法执行了...
    请求参数 name=aaa
    请求参数 time=Thu Feb 29 12:12:12 CST 2024
    拦截器的postHandle方法执行了...
    拦截器的afterCompletion方法执行了...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    至此,processDispatchResult方法执行完毕,DispatcherServlet的doDispatch方法也执行完毕,一次完整的DispatcherServlet请求处理和响应完成了。

    12.4.5 DispatcherServlet工作流程总结

    DispatcherServlet工作全流程示意图如下:

    DispatcherServlet工作全流程

    1. 客户端向服务端发起请求,由DispatcherServlet接收请求;
    2. DispatcherServlet委托HandlerMapping,根据本次请求的URL匹配合适的Controller方法;
    3. HandlerMapping找到合适的Controller方法后,组合可以应用于当前请求的拦截器,并封装为一个HandlerExecutionChain对象返回给DispatcherServlet;
    4. DispatcherServlet接收到HandlerExecutionChain对象后,委托HandlerAdapter,将该请求转发给选定的Handler;
    5. Handler接收到请求后,实际执行Controller方法;
    6. Controller方法执行完毕后返回一个ModelAndView对象给HandlerAdapter;
    7. HandlerAdapter接收到ModelAndView对象后返回给DispatcherServlet;
    8. DispatcherServlet接收到ModelAndView对象后,委托ViewResolver进行视图渲染;
    9. ViewResolver视图渲染完成后返回给DispatcherServlet,由DispatcherServlet负责响应视图。

    12.5 小结

    第12章到此就梳理完毕了,本章的主题是:SpringBoot整合WebMvc。回顾一下本章的梳理的内容:

    (三十六)SpringBoot整合WebMvc(一)@Controller控制器装配原理
    (三十七)SpringBoot整合WebMvc(二)DispatcherServlet的工作全流程

    更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

    第13章主要梳理:SpringBoot整合WebFlux。主要内容包括:

    • 响应式编程与Reactive;
    • SpringBoot整合WebFlux的快速使用;
    • SpringBoot整合WebFlux的核心自动装配;
    • DispatcherHandler的工作全流程。
  • 相关阅读:
    qt槽函数的四种写法
    java毕业设计电影网站系统设计Mybatis+系统+数据库+调试部署
    Hadoop学习记录4--Maven、HDFS API编程
    论Oracle兼容性,我们需要做什么
    Mask Free VIS笔记(CVPR2023 不需要mask标注的实例分割)
    std : : map
    mysql- 数据库的备份与还原
    Java项目:JSP民宿预订网站信息管理平台
    golang开发_goroutine在项目中的使用姿势
    Blender:对模型着色
  • 原文地址:https://blog.csdn.net/weixin_42739799/article/details/136363681