Spring MVC源码解析之HandlerMethod、ServletInvocableHandlerMethod

简介: Spring MVC源码解析之HandlerMethod、ServletInvocableHandlerMethod

InvocableHandlerMethod

增加了调用能力:在调用的时候,把方法入参都封装进,主要还是依靠 HandlerMethodArgumentResolver,只是把解析好的放到对应位置里去

public class InvocableHandlerMethod extends HandlerMethod {
  private static final Object[] EMPTY_ARGS = new Object[0];
  // 新增属性:数据绑定、数据校验
  // 用于产生数据绑定器、校验器
  @Nullable
  private WebDataBinderFactory dataBinderFactory;
  // HandlerMethodArgumentResolver用于入参的解析
  private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
  // 获取到形参名,所以注解里我们不写value,通过形参名字来匹配也可以
  private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

在给定请求的上下文中解析方法的参数值后调用该方法。 也就是说:方法入参里就能够自动使用请求域(包括path里的,requestParam里的、以及常规对象如HttpSession这种)


providedArgs

调用者可以传进来,然后直接doInvoke()的时候原封不动的使用它,弥补了请求域没有所有对象的不足,毕竟有些对象是用户自定义的。

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {
  Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  }
  // 普通的方法调用
  Object returnValue = doInvoke(args);
  return returnValue;
}

小结

该类主要提供了invoke调用目标Bean的目标方法的能力,核心的逻辑可是各种各样的HandlerMethodArgumentResolver完成

InvocableHandlerMethod这个子类虽然它提供了调用了能力,但是它却依旧还没有和Servlet的API绑定起来,毕竟使用的是Spring自己通用的的NativeWebRequest,它还有一个子类

ServletInvocableHandlerMethod

继承InvocableHandlerMethod,增加了返回值和响应状态码的处理

内部类ConcurrentResultHandlerMethod继承于它,支持异常调用结果处理,Servlet容器下Controller在查找适配器时发起调用的最终就是ServletInvocableHandlerMethod。

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
  private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
  // 处理方法返回值
  @Nullable
  private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
  // 构造函数略
  // 设置处理返回值的HandlerMethodReturnValueHandler
  public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) {
    this.returnValueHandlers = returnValueHandlers;
  }

invokeAndHandle

对invokeForRequest方法的进一步增强 因为调用目标方法还是靠invokeForRequest,本处是把方法的返回值拿来进一步处理,比如状态码

调用该方法,并通过所配置的HandlerMethodReturnValueHandler处理返回值

  public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 设置返回状态码 这里面还是有点意思的  因为@ResponseStatus#code()在父类已经解析,但子类才用
    setResponseStatus(webRequest);
    if (returnValue == null) {
      // Request的NotModified为true 有@ResponseStatus注解标注 RequestHandled=true 三个条件有一个成立,则设置请求处理完成并返回
      if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
        // 该请求已被处理
        mavContainer.setRequestHandled(true);
        return;
      }
    // 返回值不为null,@ResponseStatus存在reason 同样设置请求处理完成并返回
    } else if (StringUtils.hasText(getResponseStatusReason())) {
      mavContainer.setRequestHandled(true);
      return;
    }
    // 前边都不成立,则设置RequestHandled=false即请求未完成
    // 继续交给HandlerMethodReturnValueHandlerComposite处理
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
      this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    ...
  }
  // 设置返回的状态码到HttpServletResponse 里面去
  private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
    HttpStatus status = getResponseStatus();
    if (status == null) { // 如果调用者没有标注ResponseStatus.code()此注解  此处就忽略它
      return;
    }
    HttpServletResponse response = webRequest.getResponse();
    if (response != null) {
      String reason = getResponseStatusReason();
      // 此处务必注意:若有reason,那就是sendError  哪怕你是200哦~
      if (StringUtils.hasText(reason)) {
        response.sendError(status.value(), reason);
      } else {
        response.setStatus(status.value());
      }
    }
    // 设置到request的属性,把响应码给过去。为了在redirect中使用
    // To be picked up by RedirectView
    webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
  }
  private boolean isRequestNotModified(ServletWebRequest webRequest) {
    return webRequest.isNotModified();
  }
  // 这个方法RequestMappingHandlerAdapter里有调用
  ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
    return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
  }
  // 内部类们
  private class ConcurrentResultMethodParameter extends HandlerMethodParameter {
    @Nullable
    private final Object returnValue;
    private final ResolvableType returnType;
    public ConcurrentResultMethodParameter(Object returnValue) {
      super(-1);
      this.returnValue = returnValue;
      // 主要是这个解析 兼容到了泛型类型 比如你的返回值是List<Person> 它也能把你的类型拿出来
      this.returnType = (returnValue instanceof ReactiveTypeHandler.CollectedValuesList ?
          ((ReactiveTypeHandler.CollectedValuesList) returnValue).getReturnType() :
          ResolvableType.forType(super.getGenericParameterType()).getGeneric());
    }
    // 若返回的是List  这里就是List的类型哦  下面才是返回泛型类型
    @Override
    public Class<?> getParameterType() {
      if (this.returnValue != null) {
        return this.returnValue.getClass();
      }
      if (!ResolvableType.NONE.equals(this.returnType)) {
        return this.returnType.toClass();
      }
      return super.getParameterType();
    }
    // 返回泛型类型
    @Override
    public Type getGenericParameterType() {
      return this.returnType.getType();
    }
    // 即使实际返回类型为ResponseEntity<Flux<T>>,也要确保对@ResponseBody-style处理从reactive 类型中收集值
    // 是对reactive 的一种兼容
    @Override
    public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
      // Ensure @ResponseBody-style handling for values collected from a reactive type
      // even if actual return type is ResponseEntity<Flux<T>>
      return (super.hasMethodAnnotation(annotationType) ||
          (annotationType == ResponseBody.class && this.returnValue instanceof ReactiveTypeHandler.CollectedValuesList));
    }
  }
  // 这个非常有意思   内部类继承了自己(外部类) 进行增强
  private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
    // 返回值
    private final MethodParameter returnType;
    // 此构造最终传入的handler是个Callable
    // result方法返回值 它支持支持异常调用结果处理
    public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) {
      super((Callable<Object>) () -> {
        if (result instanceof Exception) {
          throw (Exception) result;
        } else if (result instanceof Throwable) {
          throw new NestedServletException("Async processing failed", (Throwable) result);
        }
        return result;
      }, CALLABLE_METHOD);
      // 给外部类把值设置上  因为wrapConcurrentResult一般都先调用,是对本类的一个增强
      if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) {
        setHandlerMethodReturnValueHandlers(ServletInvocableHandlerMethod.this.returnValueHandlers);
      }
      this.returnType = returnType;
    }
    ...
  }
}

HandlerMethod封装Handler和处理请求的Method

InvocableHandlerMethod增加了方法参数解析和调用方法的能力;ServletInvocableHandlerMethod在此基础上在增加了如下三个能力:

对@ResponseStatus注解的支持

1.当一个方法注释了@ResponseStatus后,响应码就是注解上的响应码。 并且,并且如果returnValue=null或者reason不为空(不为null且不为“”),将中断处理直接返回(不再渲染页面)

对返回值returnValue的处理


对返回值的处理是使用HandlerMethodReturnValueHandlerComposite完成的

对异步处理结果的处理

@Getter

@Setter

@ToString

public class Person {

@NotNull
private String name;
@NotNull
@Positive
private Integer age;
public Object demoMethod(Person person, Object object,
                         List<Integer> intList, List<Person> personList,
                         Set<Integer> intSet, Set<Person> personSet,
                         Map<String, Object> myMap,
                         String name, Integer age,
                         int number, double money) {
    return "hello parameter";
}

}

借助HandlerMethod完成此测试用例

public static void main(String[] args) {
    // 准备一个HandlerMethod
    HandlerMethod handlerMethod = new HandlerMethod(new Person(), getPersonSpecfyMethod());
    // 拿到该方法所有的参数
    MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
    for (MethodParameter parameter : methodParameters) {
        Class<?> parameterType = parameter.getParameterType();
        String nameForParameter = ModelFactory.getNameForParameter(parameter);
        System.out.println("类型" + parameterType.getName() + "--->缺省的modelKey是:" + nameForParameter);
    }
}
private static Method getPersonSpecfyMethod() {
    for (Method method : Person.class.getMethods())
        if (method.getName().equals("demoMethod"))
            return method;
    return null;
}

运行,打印结果如下:


类型com.fsx.bean.Person—>缺省的modelKey是:person

类型java.lang.Object—>缺省的modelKey是:object

类型java.util.List—>缺省的modelKey是:integerList

类型java.util.List—>缺省的modelKey是:personList

类型java.util.Set—>缺省的modelKey是:integerList // 可以看到即使是set 名称也是同List的

类型java.util.Set—>缺省的modelKey是:personList

类型java.util.Map—>缺省的modelKey是:map

类型java.lang.String—>缺省的modelKey是:string

类型java.lang.Integer—>缺省的modelKey是:integer

类型int—>缺省的modelKey是:int

类型double—>缺省的modelKey是:double

这个结果是不同类型对应的缺省的ModelKey,对理解和正确使用@SessionAttributes、@ModelAttribute都很重要


目录
相关文章
|
1月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
15天前
|
缓存 安全 Java
Spring Security通用权限管理模型解析
Spring Security作为Spring生态的核心安全框架,结合RBAC与ACL权限模型,基于IoC与AOP构建灵活、可扩展的企业级权限控制体系,涵盖认证、授权流程及数据库设计、性能优化等实现策略。
83 0
|
15天前
|
缓存 安全 Java
Spring Security权限管理解析
Spring Security是Spring生态中的核心安全框架,采用认证与授权分离架构,提供高度可定制的权限管理方案。其基于过滤器链实现认证流程,通过SecurityContextHolder管理用户状态,并结合RBAC模型与动态权限决策,支持细粒度访问控制。通过扩展点如自定义投票器、注解式校验与前端标签,可灵活适配多租户、API网关等复杂场景。结合缓存优化与无状态设计,适用于高并发与前后端分离架构。
92 0
|
22天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
29天前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
137 0
|
15天前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
273 0
|
27天前
|
Kubernetes Java 微服务
Spring Cloud 微服务架构技术解析与实践指南
本文档全面介绍 Spring Cloud 微服务架构的核心组件、设计理念和实现方案。作为构建分布式系统的综合工具箱,Spring Cloud 为微服务架构提供了服务发现、配置管理、负载均衡、熔断器等关键功能的标准化实现。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
234 0
|
29天前
|
安全 Java 数据安全/隐私保护
Spring Security 核心技术解析与实践指南
本文档深入探讨 Spring Security 框架的核心架构、关键组件和实际应用。作为 Spring 生态系统中负责安全认证与授权的关键组件,Spring Security 为 Java 应用程序提供了全面的安全服务。本文将系统介绍其认证机制、授权模型、过滤器链原理、OAuth2 集成以及最佳实践,帮助开发者构建安全可靠的企业级应用。
88 0
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。

推荐镜像

更多
  • DNS