SpringMVC 请求处理源码剖析

目录

1、请求的核心步骤与核心源码分析

1)DispatcherServlet.doDispatch()源码分析;

2)请求处理核心流程与细节:

getHandler() 根据请求如何找到处理类?

getHandler() 通过处理类如何找到适配器?

handle() 通过适配器如何执行目标方法?

2、handle()方法的执行细节

1)invokeHandlerMethod()源码分析;

2)带有@ModelAttribute的方法优先于目标方法执行源码分析;

3)方法如何确定参数值:

resolveHandlerArguments()源码.

无注解情况参数确定(部分).​

有注解的情况参数确定.

无注解的特例情况:确定POJO参数值的三步骤.

3、视图渲染页面分析 

1)视图解析源码分析;

2)View对象的核心方法render():

隐含模型中的数据放入请求域中.

4、SpringMVC的九大组件

1)九大组件何时被初始化?

2)举例:HandlerMappings的初始化 

getDefaultStrategies()默认初始化方式


 

  • 1、请求的核心步骤与核心源码分析

1)DispatcherServlet.doDispatch()源码分析;

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 {
//1、检查是否是文件上传请求(多部件)
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
//2、根据当前请求地址,找到哪个类(Controller)可以来处理
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果没有找到该类,则执行-->抛出异常
                    noHandlerFound(processedRequest, response);
                    return;
                }

//3、拿到能执行这个类的所有方法的适配器:(反射工具)
               // Determine handler adapter for the current request.
                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.简单来讲就是处理(控制)器也就是Controller中的方法被调用的地方
//注:控制器(Controller) ,处理器(Handler)
//4、这里的实质是:通过上一步拿到的适配器执行目标方法
//5、将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中,并返回ModelAndView。

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//判断是否是异步方法,如果是则结束该次请求
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

//如果该方法是void类型,则返回值无视图名,就设置一个默认的视图名,该名字为你当时过来的请求地址名
                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);
            }

//6、转发到目标页面
//根据方法最终执行完成后封装的ModelAndView;转发到页面

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


  • 2)请求处理核心流程与细节:

核心流程:
所有请求过来DispatcherServlet收到请求,调用doDispatch()方法进行处理:
1)、 getHandler():根据当前请求地址找到能够处理这个请求的目标处理器类(处理器)。
2)、getHandlerAdapter():根据当前处理类获取能够执行这个处理器方法的适配器。
3)、使用刚才获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法;
4)、目标方法执行后会返回一个ModelAndView对象
5)、根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的数据模型
6)、响应给指定页面


getHandler() 根据请求如何找到处理类?

前言:
getHandler()返回了什么?
答:会返回目标处理器类的执行链
SpringMVC 请求处理源码剖析_第1张图片

getHandler()方法中的参数processendRequest又是什么呢?
答:可以看出这个processendRequest 是来自原生的Servlet的request保存了众多变量,其中requestDispatcherPath保存了我们要请求的地址。
SpringMVC 请求处理源码剖析_第2张图片

正式 进入getHandler( )方法看一下里面具体做了些什么? 

SpringMVC 请求处理源码剖析_第3张图片
通过for循环遍历得到什么东西?
让我们看一下这个handlerMappings,发现存放了两个对象,一个是基于bean(web.xml配置),一个是基于annotation(注解),简而言之就是两个大的资源部
SpringMVC 请求处理源码剖析_第4张图片
看看这个注解的资源部封装了什么? /hello请求对应了哪个控制器(Controller)。
SpringMVC 请求处理源码剖析_第5张图片

因此在此阐述getHandler( )循环后得到了什么:
首先是下标为0的BeanNameUrlHandlerMapping对象,由于没有采用web.xml配置的方式,所以由hm.getHandler(request)方法得到的handlerMap为空 ,由此将继续遍历下标为1的DefaultAnnotationHandlerMapping,相同的方法,找到这个请求的映射信息,最终得到了目标url的处理器类

补充:handlerMap作用 在ioc容器启动创建Controller对象的时候扫描每个处理器都能处理什么请求,保存在HandllerMapping的hadnlerMap属性中;下一次请求过来,就来看哪个HandlerMapping中有这个请求的映射信息。


getHandler() 通过处理类如何找到适配器?

前言:

源码中的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 
-->mappedHandler.getHandler()就是得到处理类。
直接进入getHandler()方法看一下里面具体做了些什么?

SpringMVC 请求处理源码剖析_第6张图片
与getHandler() 方法类似,也有个for循环,最终在当前类下找到了一个能解析注解方法的适配器
SpringMVC 请求处理源码剖析_第7张图片

补充:适配器 就是用它来执行目标方法 。


handle() 通过适配器如何执行目标方法?

直接进入handle()中: 

 SpringMVC 请求处理源码剖析_第8张图片

 404行:判断这个处理类这否有sessionAttributes注解.
中间非核心部分省略....

最核心的428行:invokeHandlerMethod(request, response, handler)

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
//拿到方法的解析器
        ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
//方法解析器跟据当前请求地址找到真正的目标方法
        Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//创建方法执行器
        ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
//包装原生的(request,response)
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
//创建了一个BindingAwareModelMap() 就是隐含模型
        ExtendedModelMap implicitModel = new BindingAwareModelMap();
//真正执行目标方法:目标方法利用反射执行期间确定参数值,提前执行modelattribute等所有操作
       Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
        ModelAndView mav =
                methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
        methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
        return mav;
    } 

  • 2、handle()方法的执行细节

1)invokeHandlerMethod()源码分析;

2)带有@ModelAttribute的方法优先于目标方法执行源码分析;

    public final Object invokeHandlerMethod(Method handlerMethod, Object handler,NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { 
//得到待执行的方法包含方法参数
        Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
        try {
            boolean debug = logger.isDebugEnabled();
            for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
                Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
                if (attrValue != null) {
                    implicitModel.addAttribute(attrName, attrValue);
                }
            }
//先找到该类所有有@ModelAttribute注解的方法(意思就是要先执行此方法)
            for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
             Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);


//确定@ModelAttribute方法执行时使用的每一个参数值,并且传递了一个implicitModel(隐含模型)
                Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
                if (debug) {
                    logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
                }


//1、对@ModelAttribute的实际操作例如:添加到隐含模型中。
                String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
                if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
                    continue;
                }
                ReflectionUtils.makeAccessible(attributeMethodToInvoke);
//@ModelAttribute最终通过反射调用执行了
                Object attrValue = attributeMethodToInvoke.invoke(handler, args);
                if ("".equals(attrName)) {
                    Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
                    attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
                }
                if (!implicitModel.containsAttribute(attrName)) {
                    implicitModel.addAttribute(attrName, attrValue);
                }
            }

//与上面类似,确定目标方法执行时使用的每一个参数值
            Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
            if (debug) {
                logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
            }
            ReflectionUtils.makeAccessible(handlerMethodToInvoke);
//2、经过对@ModelAttribute的方法执行后,这里才是真正的执行目标方法....
            return handlerMethodToInvoke.invoke(handler, args);
        }
        catch (IllegalStateException ex) {
            // Internal assertion failed (e.g. invalid signature):
            // throw exception with full handler method context...
            throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
        }
        catch (InvocationTargetException ex) {
            // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
            ReflectionUtils.rethrowException(ex.getTargetException());
            return null;
        }
    } 

3)方法如何确定参数值:

resolveHandlerArguments()源码.

代码概述:首先会获得参数的基本信息,再循环遍历每个参数,为其保存参数信息。

注意:在循环遍历的时候,会判断是否包含有注解,有注解的参数无注解的参数将区分对待
SpringMVC 请求处理源码剖析_第9张图片


无注解情况参数确定(部分).
SpringMVC 请求处理源码剖析_第10张图片

具体源码:
1)resolveCommonArgument()方法实质是在用resolveStandardArgument()解析标准参数的方法:判断是否为原生API

SpringMVC 请求处理源码剖析_第11张图片
SpringMVC 请求处理源码剖析_第12张图片

SpringMVC 请求处理源码剖析_第13张图片

2)如果不是则判断是否为Model或者Map,如果是的话将implicitModel(隐含模型)传给参数args[ ],
SpringMVC 请求处理源码剖析_第14张图片

3)再看是否是其他类型(包含简单类型)
SpringMVC 请求处理源码剖析_第15张图片


有注解的情况参数确定.

SpringMVC 请求处理源码剖析_第16张图片

 

如果在无注解且不是上述的几种情况之外:例如(POJO自定义类型)
注:在判断无注解情况,再不满足上述情况下,attrName=" ";
SpringMVC 请求处理源码剖析_第17张图片

无注解的特例情况:确定POJO参数值的三步骤.

    private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
            ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {

        // Bind request parameter onto object...
        String name = attrName;
        if ("".equals(name)) {
//如果attrName是空串:就将参数类型的首字母小写作为值:例如Book-->book
            name = Conventions.getVariableNameForParameter(methodParam);
        }
        Class paramType = methodParam.getParameterType();
        Object bindObject;
//1、如果隐含模型中有这个key(标了@ModelAttribute注解就是注解指定的value,没标就是参数类型的首字母小写),作为该目标对象。
        if (implicitModel.containsKey(name)) {
            bindObject = implicitModel.get(name);
        }

//2、如果没有,则看@SessionAttribute标注的属性,就从session中拿对象,作为目标对象。
        else if (this.methodResolver.isSessionAttribute(name, paramType)) {
            bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
            if (bindObject == null) {
                raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
            }
        }

//3、如果都不是,则通过反射创建目标对象
        else {
            bindObject = BeanUtils.instantiateClass(paramType);
        }
        WebDataBinder binder = createBinder(webRequest, bindObject, name);
        initBinder(handler, name, binder, webRequest);
        return binder;
    }

 

前言:
在说视图解析之前,必须得说清之前说过的请求处理核心流程与细节中的目标方法执行完后会返回ModelAndView对象

SpringMVC 请求处理源码剖析_第18张图片

 

 因此得到结论:任何方法的返回值,最终都会包装成ModelAndView对象(并且都会有一个视图名属性)。

  • 3、视图渲染页面分析 

视图渲染页面=视图解析+视图对象执行render() 

何为视图渲染?--->将域中的数据在页面中展示(页面就是来渲染模型数据的)

 SpringMVC 请求处理源码剖析_第19张图片


1)视图解析源码分析;

视图解析:就是得到View(视图对象)的过程

 SpringMVC 请求处理源码剖析_第20张图片

SpringMVC 请求处理源码剖析_第21张图片
SpringMVC 请求处理源码剖析_第22张图片

SpringMVC 请求处理源码剖析_第23张图片 

注:这里就不过多说明父类的一些列繁琐的创建View过程,例如:拼串等等...  

SpringMVC 请求处理源码剖析_第24张图片


2)View对象的核心方法render():

SpringMVC 请求处理源码剖析_第25张图片 

隐含模型中的数据放入请求域中.

SpringMVC 请求处理源码剖析_第26张图片 

SpringMVC 请求处理源码剖析_第27张图片 

 最终进行请求转发:

SpringMVC 请求处理源码剖析_第28张图片 


 

  • 4、SpringMVC的九大组件

SpringMVC 请求处理源码剖析_第29张图片

	/** 文件上传解析器 */
	private MultipartResolver multipartResolver;

	/** 区域信息解析器:和国际化有关 */
	private LocaleResolver localeResolver;

	/**主题解析器:强大的主题效果更换 */
	private ThemeResolver themeResolver;

	/** Handler映射信息:HandlerMapping */
	private List handlerMappings;

	/** Handler的适配器 */
	private List handlerAdapters;

	/**SpringMVC强大的异常解析功能:异常解析器 */
	private List handlerExceptionResolvers;

	/** RequestToViewNameTranslator used by this servlet */
	private RequestToViewNameTranslator viewNameTranslator;

	/** Flash+MapManager:SpringMVC中运行重定向携带数据的功能 */
	private FlashMapManager flashMapManager;

	/** 视图解析器 */
	private List viewResolvers;

1)九大组件何时被初始化?

还是在DispatcherServlet中的onRefresh()方法,可以发现有@Override实是Spring启动ioc容器的时候,这个onRefresh()方法被调用。

SpringMVC 请求处理源码剖析_第30张图片

2)举例:HandlerMappings的初始化 

SpringMVC 请求处理源码剖析_第31张图片

 
首先getBean通过id为handlerMapping去IOC容器中找,如果为没有找到为null,则采用默认初始化去找。

getDefaultStrategies()默认初始化方式

1、让我们来看看getDefaultStrategies()是如何找的:从defaultStrategies中通过key找到value。
SpringMVC 请求处理源码剖析_第32张图片

2、 有一个静态代码块,ClassPathResource类路径下的资源,第一个参数是:资源名,第二个参数是:此参数路径下,
找到后在加载这个资源的配置。 

SpringMVC 请求处理源码剖析_第33张图片

 3、

4、 最终加载的默认资源路径就是:
SpringMVC 请求处理源码剖析_第34张图片

文件中的内容:
SpringMVC 请求处理源码剖析_第35张图片

 补充:这也得到了前面处理请求核心细节中getHandler()方法获得处理类时,有这两个对象。

SpringMVC 请求处理源码剖析_第36张图片

你可能感兴趣的:(springmvc,源码)