• SpringMVC源码解析


    HandlerMethod及子类主要用于封装方法调用相关信息,子类还提供调用,参数准备和返回值处理的职责.

    分析下各个类的职责吧(顺便做分析目录):

    HandlerMethod 封装方法定义相关的信息,如类,方法,参数等.

      使用场景:HandlerMapping时会使用

    InvocableHandlerMethod 添加参数准备,方法调用功能

      使用场景:执行使用@ModelAttribute注解会使用

    ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus处理

      使用场景:执行http相关方法会使用,比如调用处理执行

    1. HandlerMethod

     HandlerMethod其实可以简单理解为保持方法信息的pojo.

    所以这边主要就是看下定义的属性:

     1 package org.springframework.web.method;
     2 public class HandlerMethod {
     3     /** 什么鬼,给子类提供logger,到现在为止源码中不多见 */
     4     protected final Log logger = LogFactory.getLog(HandlerMethod.class);
     5     // 方法所在的类,如果是String类型,可以去容器中获取
     6     private final Object bean;
     7     // 方法
     8     private final Method method;
     9     // 类管理的容器
    10     private final BeanFactory beanFactory;
    11     // 方法的参数
    12     private final MethodParameter[] parameters;
    13     // 如果方法是bridged方法,则对应原始方法
    14     private final Method bridgedMethod;
    15     // ...
    16 }


    大部分应该是看看注释就能理解了,我们解释下下面:

      这边所有的熟悉都是final类型的,不可修改,所以如果出现修改需要new.

      如果bean是string,是在createWithResolvedBean找容器获取实例的.

      MethodParameter类封装了参数相关的信息.

      提供获取返回值,判断是否void类型,还有读取注解

    createWithResolvedBean逻辑其实很简单:

    确认下如果bean是String类型的,那么从容器BeanFactory中获取,并使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一个新的.

    1 // HandlerMethod
    2     public HandlerMethod createWithResolvedBean() {
    3         Object handler = this.bean;
    4         if (this.bean instanceof String) {
    5             String beanName = (String) this.bean;
    6             handler = this.beanFactory.getBean(beanName);
    7         }
    8         return new HandlerMethod(this, handler);
    9     }

    MethodParameter,可以根据method方法和parameterIndex(参数下标)唯一确定,其他属性都可以根据他们两获取.

    由于反射中没有参数名的信息,而这边需要,所以Spring添加了一个参数名查找器ParameterNameDiscover.

    这边返回值的类型是存储在parameters属性中的,下标用-1区分.

    MethodParameter在HandlerMethod有两个内部类的子类.

     1 package org.springframework.core;
     2 public class MethodParameter {
     3     // 参数所在方法
     4     private final Method method;
     5     // 参数的构造方法
     6     private final Constructor constructor;
     7     // 参数下标
     8     private final int parameterIndex;
     9     // 参数类型
    10     private Class<?> parameterType;
    11     // Type类型的参数类型
    12     private Type genericParameterType;
    13     // 参数使用的注解
    14     private Annotation[] parameterAnnotations;
    15     // 参数名查找器
    16     private ParameterNameDiscoverer parameterNameDiscoverer;
    17     // 参数名
    18     private String parameterName;
    19     // 参数嵌套级别,如Map<String>中map为1,string为2
    20     private int nestingLevel = 1;
    21     // 每层的下标
    22     /** Map from Integer level to Integer type index */
    23     Map<Integer, Integer> typeIndexesPerLevel;
    24     // 什么鬼?就一个构造方法用了,其他都没有使用
    25     Map<TypeVariable, Type> typeVariableMap;
    26     // ... 
    27 }

    2. InvocableHandlerMethod

    习惯性的,看到Invocable,会想到Spring会不会定义一个接口来定义可调用概念,不过没有.

    这边添加了2个职责:参数准备和方法执行.

    参数准备委托HandlerMethodArgumentResolver进行具体的解析.解析的时候需要用到WebDataBinder,所以顺便带上.对参数解析器有兴趣可以移步这里

    2.1 先来看看参数准备工作部分吧.

    查找某个参数值的逻辑:

      a, 先委托参数名查找器获取参数名

      b,从外部提供的参数清单中查找值(竟然是根据类型判断的)

      c,如果没有直接提供,使用参数解析器创建

      d,如果还是没有获得,直接报错

     1 package org.springframework.web.method.support;
     2 
     3 public class InvocableHandlerMethod extends HandlerMethod {
     4     // 参数解析器
     5     private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
     6     // 参数解析器需要用到
     7     private WebDataBinderFactory dataBinderFactory;
     8     // 参数名查找器
     9     private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
    10 
    11     private Object[] getMethodArgumentValues(
    12             NativeWebRequest request, ModelAndViewContainer mavContainer,
    13             Object... providedArgs) throws Exception {
    14 
    15         MethodParameter[] parameters = getMethodParameters();
    16         Object[] args = new Object[parameters.length];
    17         for (int i = 0; i < parameters.length; i++) {
    18             MethodParameter parameter = parameters[i];
    19             parameter.initParameterNameDiscovery(parameterNameDiscoverer);
    20             // 查找参数名
    21             GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
    22             // 从提供的参数值providedArgs中找值
    23             args[i] = resolveProvidedArgument(parameter, providedArgs);
    24             if (args[i] != null) {
    25                 continue;
    26             }
    27             // 使用参数解析器解析
    28             if (argumentResolvers.supportsParameter(parameter)) {
    29                 try {
    30                     args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
    31                     continue;
    32                 } catch (Exception ex) {
    33                     throw ex;
    34                 }
    35             }
    36             // 参数获取不到是需要报错的
    37             if (args[i] == null) {
    38                 String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
    39                 throw new IllegalStateException(msg);
    40             }
    41         }
    42         return args;
    43     }
    44 
    45     private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
    46         if (providedArgs == null) {
    47             return null;
    48         }
    49         for (Object providedArg : providedArgs) {
    50             // 竟然是根据类型判断的
    51             if (parameter.getParameterType().isInstance(providedArg)) {
    52                 return providedArg;
    53             }
    54         }
    55         return null;
    56     }
    57 
    58     // ...
    59 
    60 }

     2.2 方法执行

    这边的逻辑其实很简单:

      委托获取方法执行需要的参数

      强制将方法变为可用

      处理方法执行过程中的异常

     1 package org.springframework.web.method.support;
     2 
     3 public class InvocableHandlerMethod extends HandlerMethod {
     4     // ... 
     5     public final Object invokeForRequest(NativeWebRequest request,
     6                                          ModelAndViewContainer mavContainer,
     7                                          Object... providedArgs) throws Exception {
     8         Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
     9         Object returnValue = invoke(args);
    10         return returnValue;
    11     }
    12 
    13     private Object invoke(Object... args) throws Exception {
    14         ReflectionUtils.makeAccessible(this.getBridgedMethod());
    15         try {
    16             return getBridgedMethod().invoke(getBean(), args);
    17         }
    18         catch (IllegalArgumentException e) {
    19             String msg = getInvocationErrorMessage(e.getMessage(), args);
    20             throw new IllegalArgumentException(msg, e);
    21         }
    22         catch (InvocationTargetException e) {
    23             // Unwrap for HandlerExceptionResolvers ...
    24             Throwable targetException = e.getTargetException();
    25             if (targetException instanceof RuntimeException) {
    26                 throw (RuntimeException) targetException;
    27             }
    28             else if (targetException instanceof Error) {
    29                 throw (Error) targetException;
    30             }
    31             else if (targetException instanceof Exception) {
    32                 throw (Exception) targetException;
    33             }
    34             else {
    35                 String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
    36                 throw new IllegalStateException(msg, targetException);
    37             }
    38         }
    39     }
    40 
    41 }

    3. ServletInvocableHandlerMethod

      委托HandlerMethodReturnValueHandler添加返回值处理功能

      添加@ResponseStatus注解支持.

    这边使用的@ResponseStatus注解两个属性:value状态码,reason 写入response的说明文字

    3.1 设置response status时的逻辑:

      responseStatus没设置就返回

      responseReason存在则进入error

      把responseStatus设置到request,RedirectView需要使用 

     1 // ServletInvocableHandlerMethod
     2     private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
     3         if (this.responseStatus == null) {
     4             return;
     5         }
     6 
     7         if (StringUtils.hasText(this.responseReason)) {
     8             webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
     9         }
    10         else {
    11             webRequest.getResponse().setStatus(this.responseStatus.value());
    12         }
    13 
    14         // to be picked up by the RedirectView
    15         webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
    16     }

    3.2 invokeAndHandle

      委托父类执行请求

      添加ResponseStatus支持

      然后判断怎么样才是执行完毕,满足一下任意一个:

        request的notModified为真,使用@ResponseStatus注解,mavContainer的requestHandled为真

      委托HandlerMethodReturnValueHandler封装返回值

     1 // ServletInvocableHandlerMethod
     2     public final void invokeAndHandle(ServletWebRequest webRequest,
     3             ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
     4 
     5         Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
     6 
     7         setResponseStatus(webRequest);
     8 
     9         if (returnValue == null) {
    10             if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
    11                 mavContainer.setRequestHandled(true);
    12                 return;
    13             }
    14         }
    15         else if (StringUtils.hasText(this.responseReason)) {
    16             mavContainer.setRequestHandled(true);
    17             return;
    18         }
    19 
    20         mavContainer.setRequestHandled(false);
    21 
    22         try {
    23             this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    24         }
    25         catch (Exception ex) {
    26             if (logger.isTraceEnabled()) {
    27                 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    28             }
    29             throw ex;
    30         }
    31     }
  • 相关阅读:
    生成TXT下载并以逗号分隔
    使用javascript绑定键盘enter事件到asp.net的button控件 .
    学习wpf播放视频音频的两种不同方法
    批量修改数据库表的架构sql
    如何修改Sql2005注册服务器名称
    虚拟化之Hypervisor
    JAVA环境配置
    centos系统网卡配置详解
    Kali Linux安装
    Linux扩容新增磁盘分区挂载fdisk
  • 原文地址:https://www.cnblogs.com/leftthen/p/5229204.html
Copyright © 2020-2023  润新知