通俗易懂聊springMVC中的handler是什么
  wppYuBI3bZta 2023年12月12日 23 0

了解过SpingMVC流程的同学一定听说过handler,那handler到底是什么?百度翻译过来是处理者,很多文章中称之为处理器.那就按照大部分人的说法称呼它为控制器.说到控制器,会不会联想到我们平常写业务代码中的各种controller,也是控制器,是不是一种东西啊,这里可以大胆猜测一下就是一种东西嘛.好,现在通过源码进行验证猜测!

如果直接从源码中按照类文件类型直接搜索Handler是找不到的,根据springMVC的工作流程开始捋(有很多文章说过这里不在重述),最早出现handler是在这个地方:
AbstractHandlerMapping.java中getHandler(),源码如下:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
  // 获取handler,如果获取为空则使用默认handler,如果默认的也没有则返回null
    Object handler = getHandlerInternal(request);
    // 省略部分代码....
  
}

测试案例发送实际请求:

@RequestMapping("/test")
@RestController
public class Test {
  
  @GetMapping("/add")
  public String add(String a,Personal personal,@RequestParam(name = "genderType") int gender){
    int i=0;
    return a;
  }
}

对应请求来看一下debugg,Object handler = getHandlerInternal(request)中handler具体是什么内容,截图如下:


通俗易懂聊springMVC中的handler是什么_启动过程


从中可以看到handler实际上一个HandlerMethod类型对象,里面的属性有请求所在的类信息、请求方法、请求参数等内容。所以从这里可以认为handler相当于是平常业务代码中每个请求对应的的controller类以及方法信息.上面debug截图对应起来更容易理解!

handler是如何获取的

项目启动过程中会有包扫描,与本文相关的扫描内容是将类上标注@RequestMapping的类信息以及方法上带有@RequestMapping的方法信息扫描到容器中(注意符合restful风格的@GetMapping、@PostMapping等标注的方法信息也会扫描进去,下文会有介绍).每个带有以上注解的方法扫描到容器中都会有一个RequestMappingInfo(请求映射信息,实际上就是注解中各项属性和属性值的数据组装信息)。

具体请求过程实际上是从Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>中根据请求对应的RequestMappingInfo获取对应的MappingRegistration,其中含有handler(实际上是HandlerMethod)。

先说项目启动,扫描@RequestMapping处理核心处理

RequestMappingHandlerMapping.java中getMappingForMethod(),源码如下:

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    
  // 根据方法上标注@RequestMapping注解获取RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
  
    if (info != null) {
        // 根据方法所在类上标注@RequestMapping注解获取RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
        // 将按照类获取的RequestMappingInfo 与方法获取的RequestMappingInfo 进行组装生成新方法级别上的RequestMappingInfo
        info = typeInfo.combine(info);
      }
      
      // 省略部分代码....
    }
    return info;
}

根据类信息或是方法信息创建RequestMapping的处理:
RequestMappingHandlerMapping.java中createRequestMappingInfo(),源码如下:

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
  // 获取类或是方法上带有@RequestMapping注解的RequestMapping注解信息(@GetMaping、@PostMapping等都是按照@RequestMapping进行解析,此类注解只不过是指定method为对应的GTE或POST请求方式的@RequestMapping)
  RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
  
  // 获取类或是方法上自定义请求条件,此处都返回null
  RequestCondition<?> condition = (element instanceof Class ?
  getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
  
  return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

具体组装RequestMappingInfo 逻辑:
RequestMappingHandlerMapping.java中createRequestMappingInfo()的重载方法

protected RequestMappingInfo createRequestMappingInfo(
    RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
  
    // 此处是读取@RequestMapping注解中各种属性值.比如说请求路径信息path、请求头信息headers、请求方式信息method等信息。
    RequestMappingInfo.Builder builder = RequestMappingInfo
    .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
    .methods(requestMapping.method())
    .params(requestMapping.params())
    .headers(requestMapping.headers())
    .consumes(requestMapping.consumes())
    .produces(requestMapping.produces())
    .mappingName(requestMapping.name());
    if (customCondition != null) {
   	 builder.customCondition(customCondition);
    }
  
    return builder.options(this.config).build();
}

至此项目启动,能扫描到的类中所有带有@RequestMapping注解的方法都有一个RequestMappingInfo创建加载完成。

发送请求获取HandlerMethod 逻辑,
AbstractHandlerMethodMapping.java中getHandlerInternal,源码如下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  
  // 获取请求路径,此处不做展开介绍
    String lookupPath = initLookupPath(request);
  
    // 省略部分代码
    try {
        // 根据请求路径以及请求信息获取HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    // 省略部分代码
}

获取的具体逻辑在
AbstractHandlerMethodMapping.java中lookupHandlerMethod,源码如下:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    
  List<Match> matches = new ArrayList<>();
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
  
  if (directPathMatches != null) {
   		 addMatchingMappings(directPathMatches, matches, request);
    }
  
    if (matches.isEmpty()) {
    		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
  
    if (!matches.isEmpty()) {
        Match bestMatch = matches.get(0);
        // 省略部分代码
        bestMatch.getHandlerMethod());
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.getHandlerMethod();
    }
    // 省略部分代码
}

梳理一下上面的逻辑:HandlerMethod由
bestMatch.getHandlerMethod()获取,bestMatch由`matches.get(0)获取,matches是由addMatchingMappings(directPathMatches, matches, request)进行封装处理而来.下面就继续看addMatchingMappings方法.

AbstractHandlerMethodMapping.java中addMatchingMappings(),源码如下:

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
       T match = getMatchingMapping(mapping, request);
        if (match != null) {
        matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
        }
    }
}

从上面可以发现matches中由Match组装而来,查看Match实现发现HandlerMethod是由getHandlerMethod()而来(见Match实现).到这终于搞清楚HandlerMethod的最终出处.

private class Match {
  
    private final T mapping;
    private final MappingRegistration<T> registration;

    public Match(T mapping, MappingRegistration<T> registration) {
      this.mapping = mapping;
      this.registration = registration;
    }

    public HandlerMethod getHandlerMethod() {
    	return this.registration.getHandlerMethod();
    }
    // 省略部分代码.....
}

那问题来了,映射注册器中MappingRegistration中如何进行组装的HandlerMethod.不要着急请继续往下看.

分析发现项目启动过程中,容器会将所有''认为''是handler的bean(实际就是带有@Controller或是@RequestMapping的类,至于为什么下面有讲)中的方法对象method与映射对象信息RequestMapping进行做注册匹配.
RequestMappingHandlerMapping.java中isHandler,源码如下:

protected boolean isHandler(Class<?> beanType) {
  //定义了哪种beanType符合Handler.即类中有@Controller或是@RequestMapping注解
  return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
  AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

注册匹配具体处理:

将handler与对应的requestMapping做对应匹配放到注册器中.

public void register(T mapping, Object handler, Method method) {

    // 省略部分代码....
    // 按照handler与method对象进行组装HandlerMethod.(两个参数对应测试案例中的类的beanName:test,以及请求方法对象:public java.lang.String com.it.txm.demo.methodReslove.Test.add(java.lang.String,com.it.txm.demo.methodReslove.Personal,int));创建过程就是简单的构造函数创建,不深入分析.
    HandlerMethod handlerMethod = createHandlerMethod(handler, method);

    // 省略部分代码....
    // 将requestMappingInfo和包含handlerMethod的信息组装到映射MappingRegistry
    this.registry.put(mapping,
    new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));

}

至此handlerMethod的来龙去脉已经梳理完毕!

handler简单总结

handler相当于是平常业务代码中每个请求对应的的controller类以及方法信息.文章第一部分的debug截图对应起来更容易理解!

handler来源分析总结:

1.项目启动过程中将所带有@RequestMapping注解的方法对应创建一个requestMappingInfo对象(存储注解中各属性信息);

2.项目启动过程中,容器会将所有的认为是handler的bean中的每个方法进行匹配处理,期间会创建handlerMethod,将映射信息RequestMappingInfo与MappingRegistration(其中包含handlerMethod)进行组装map处理,前者为key,后者为value.

3.发送请求,解析请求信息,从Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>中根据RequestMappingInfo获取MappingRegistration信息,在从中获取HandlerMethod.

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月12日 0

暂无评论

推荐阅读
wppYuBI3bZta