SpringBoot2-9第二版,再重新总结一遍 注释解析原理 支持27种参数注释,15种返回值 循环目标参数-->找到参数解析器--》用解析器去缓存uriTempl中获取name,根据name获取值

  html 页面请求:

           /car/{id}/owner/{username}

Step1.解析注释入口类

         org.springframework.web.servlet   DisPatcherServlet Class

              1) doDispatch方法

                       WebAsyncManager asyncManager属性

                      继续stepover

              2)找到请求对应的handler

放行到入口程序第1步:

mappedHandler = this.getHandler(processedRequest);

进入this.getHandler

        size=4这里,里面有mappingRegistry,可以看到里面有处理页面请求的handler(封装了controller方法信息)

   一路放行,回到调用处,可以看到mappinghandler已经找到了,

同时也留意  handler=(HandlerMethod@6911)代表这个handler类型是HandlerMethod

         3) 为handler找到适配器---大的反射工具HandlerAdapter,可以自定义

                     因为已经到了方法,接下来要用反射调用方法,给方法的参数赋值,这个过程被封装到了HandlerAdapter

                   

放行到入口程序第2.1步:ha获取
 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

3.1)ctrl进入HandlerAdapter类查看发现这是一个接口

 

 声明支持处理的handler类型

    boolean supports(Object handler);

 如果支持的话就调用

   

 @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

3.2)stepinto  getHandlerAdapter查看寻找适配器原理

         发现有4种适配器

            

       0-支持标注了@Request的方法

       1-支持Controller里面放了函数的

    轮询适配器,判断当前适配器支不支持当前handler

 如何判断呢?stepinto

 以上要求handler类型必须是(HandlerMethod),而上文mappiing到的handler也是HandlerMethod

所以找到了是就是4大适配器中的第1个RequestMappingHandler,放行返回

继续放行,下面一句判断是不是Get方法:   true 

   

 再判断是不是Head:

    如果是head可能就不需要服务器处理,如果有浏览器缓存处理浏览器缓存

放行到入口程序第2.2步:  ha去处理

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

 (processedRequest, response, 目标方法:mappedHandler.getHandler())

mappedHandler.getHandler():

 了解如何执行目标方法,stepinto getHandler

     首先获取handler

放行到入口程序第3步:mv=ha.handle()方法

 再点击stepinto 默认进入即可

   

 在上面Stepinto,这是属于RequestMappingHandlerAdapter类下的

     放行,就到了执行目标方法处 RequestMappingHandlerAdapter类下 mav = this.invokeHandlerMethod(request, response, handlerMethod);

 stepinto

再放行

 其中创建了invocableMethod

          ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);

本质是给目标方法getCar再包装了一层

 4.2)确定要执行目标方法的每个参数的值

接下来为invocableMethod创建了参数解析器    argumentResolvers

invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);

这27个都是以MethodArgumentResolver结尾,前面就代表controller 方法中的参数注释@

 ctrl查看argumentResolvers

 继续ctrl看其父类或接口

查看List<HandlerMethodArgumentResolver>父类

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);

    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);

    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

在 HandlerMethodArgumentResolver上ctrl+F12查看其方法

 有两个方法

supportsParameter,确定是否支持这种参数,如果支持就调用方法2 resolveArgument进行解析

 4.3)返回值处理器

继续放行到  returnValueHandlers

 这个决定了controller里面方法返回值类型

   比如如下是Map返回值类型

 可以看到支持15种返回值处理器

 4.4)真正执行目标方法(Controller里面的)  invocableMethod

   以上两个处理器都被封装在invocableMethod

ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);

 

 继续放行到 本类(RequestMappingHandlerAdapter)的

invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

 继续Step into invokeAndHandle,则进入到另外一个类(ServletInvocableHandlerMethod

)

 

 ps :   放行之后即会去访问controller--实际未观察到

现在stepinto  上面语句

 第一步获取方法所有参数    getMethodArgumentValues

 Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);

stepinto

4.5)如何确定目标方法每一个参数的值

代码位于   org.springframework.web.method.support -->InvocableHandlerMethod

   protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        } else {
            Object[] args = new Object[parameters.length];

            for(int i = 0; i < parameters.length; ++i) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
                    if (!this.resolvers.supportsParameter(parameter)) {
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
                        if (logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }

先获取到所有的方法声明:

    

 MethodParameter[] parameters = this.getMethodParameters();

放行此句之后查看,可以看到

第0个参数标记了什么注解:anntation.PathVariable

该参数paramIndex是0

该参数类型parametertype没显示出来 

 再看第三个参数类型是RequestHeader

 可见这些参数是与controller中顺序和总数是对应的,一共8个

 继续放行,如果无参数列表直接返回

   if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;

如果有参数列表,则新建一共与参数数量相同的Object数组

           Object[] args = new Object[parameters.length];

  放行之后可以见到,length长度为8,正好等于上文所述参数

 开始遍历,先拿到第0个参数,也就是名称为 id的这个 

 继续放行,判断是否是参数解析器支持的类型

 当前(Parameter)值是参数0,并且注解是PathVariable  如下显示:

 再次看下解析器reslovers还是那27个

 Stepinto    

进入了

 继续Stepinto,进入  org.springframework.web.method.support包                   HandlerMethodArgumentResolverComposite类

 HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);

一开始result 是null,没有从  argumentResolverCache获取到

因为result是空的,所以进入遍历


        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, resolver);
                    break;
                }
            }
        }

        return result;
    }

遍历这27个参数解析器

可以看到这个参数paramter就是controller里面的参数0,也就是在27个参数解析器中寻找支持参数0的

 第一轮---先看第一个参数解析器RequestParamMethodArgument支不支持目标方法参数0

 stepinto 进入了 org.springframework.web.method.annotation包的supportsParameter方法

 判断原理就是传进来的参数0有没有标记RequestParam注解

if (parameter.hasParameterAnnotation(RequestParam.class)) {

因为我们的参数0 "id"标的是@PathVariable,所以本轮循环失败

  放行一轮,直到再次进入以下if语句: 

进入第二轮

 

 一样Null返回

进入第三轮

 stepinto

 先判断表没标PathVariable.class,因为目标参数0标记了pathvariable,进入else if

   不是map,所以返回true,继续放行,返回到了调用处

可以看到result已经有值了

 把它放到缓存里,所以第一次请求Spring MVC很慢,之后用缓存的就很快

this.argumentResolverCache.put(parameter, resolver);

 4.5.2)解析参数值

继续放行

 在上述位置Stepinto并放行

可以ctrl 进入到getArgumentResolver所在类查看一眼

 继续放行到66行

Stepinto 

 

 放行到这里

 1)获取名字

NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);

    

 接下来解析参数名

 Object resolvedName = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);

在以下位置Stepinto    ,

Object resolvedName = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);

进入了 org.springframework.web.method.annotation包的AbstractNamedValueMethodArgumentResolver类

 可见里面有各种解析器,比如placeholdersResolved,最后返回名字

继续放行到返回到调用处并到达获取值语句

Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);

 Stepinto默认进入

 放行并查看uriTemplateVars

 回忆uriTemplateVars值来自于  UrlPathHelper类形成的缓存

 

 回到reoslveName查看  uriTemplateVars

 继续放行回到resolveArgument,至此拿到了名字为“id” 的arg=3

 终于确定了目标参数第一个参数的值------------------------------------------------------------

  一路放行回到InvocableHandlerMethod

放行  可以看到args已经有一个值了

 

 同理接下来遍历其他所有参数

继续循环,可见i已经等于1

 要寻找的参数类型可见是 PathVariable

 继续放行寻找支持该参数的解析器

 放行到85行

stepinto

 放行,可以看到已经拿到解析器

 放行,开始解析值

 stepinto

继续放行,看到已经拿到name值  白居易

继续放行回到   InvocableHandlerMethod,可见已经获取了两个参数

 ------------------------------------在验证另外一种类型参数------------------------

继续放行到i=3

 这是@RequestHeader

 放行到

 Stepinto

 再继续Stepinto

 放行,第一次判断前result为空

 开始用第一个resolver   RequestParamMethodArgumentResolver判断

 放行继续第二轮Resolver解析器RequestParamMapMethodArgumentResolver---判断

可以看到argumentResolvers.iterator()中argumentResolvers有27个

 继续放行第3轮Resolver解析器PathVariableMethodArgumentResolver

 继续放行第4轮PathVariableMapMethodArgumentResolver

 继续第5轮MatrixVariableMethodArgumentResolver

 继续第6轮MatrixVariableMapMethodArgumentResolver

  继续第7轮ServletModelAttributeMethodProcessor

   继续第8轮RequestResponseBodyMethodProcessor

    继续第8轮RequestPathMethodArgumentResolver

 继续第9轮RequestHeaderMethodArgumentResolver

 Stepinto  只要注解中有RequestHeader并且不是Map就会支持

 而我们的目标方法参数第3个就写了@RequestHeader

 继续放行,可以看到result已经被赋值,之后放到缓存

 现在已经拿到解析器,放行回到  InvocableHandlerMethod

 stepinto开始获取值

 Stepinto  从缓存拿到解析器

 

 继续放行

 Stepinto

 继续放行,首先拿到这个参数的名字

 

 再去拿到该名字对应的值

 Stepinto调用Servlet原生Request,用getHeaderValues

 继续放行,可以看到   headerValues已经获取

headerValues已经获取

 放行,回到

 放行,可以看到arg已经有值

继续其他

放行之后args获得了页面请求的所有参数

 之后放行doInvoke利用反射调用目标方法

总结如下Visio

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值