资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--HandlerMapping(四)

imccl / 2671人阅读

摘要:默认支持该策略。以上是对的宏观分析,下面我们进行内部细节分析。整体流程一通过实现接口,完成拦截器相关组件的初始化调用类的方法。总结本文主要分析了的初始化过程,希望对大家有帮助。随着学习的深入,后面有时间在分析下期中涉及的关键,比如等等。

概述

本节我们继续分析HandlerMapping另一个实现类ReqeustMappingHandlerMapping,该类是我们日常开发中使用最多的映射器策略,即我们在开发中使用的注解开发方式,如:@Controller、@RequestMapping等,都使用的是此映射策略。Spring MVC默认支持该策略。

本系列文章是基于Spring5.0.5RELEASE。

类图

类的继承关系,如下图:

第一次看到此图可能会感觉好复杂,大家别急,学技术就是这样,首先需要静下心,再一个要学会对已掌握知识点做总结、对比、分类,这样才能把所有的知识点串起来,能系统的了解一项技术。

以上是我个人的一些经验,希望对别人能有帮助,废话不多说了,我们来分析下这张图,之前我们学过了SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping,通过横向对比,我们发现,他们都继承自AbstractHandlerMapping抽象类,而AbstractHandlerMapping类的主要工作就是初始化拦截器的功能,三者的实现都是一样的。

继续分析,我们发现RequestMappingHandlerMapping增加实现了InitializingBean和EmbeddedVualeResolverAware接口,即增加了如下能力:

实现InitializingBean接口,增加了bean初始化的能力,也就是说在bean初始化时可以做一些控制

实现EmbeddedValueResolverAware接口,即增加了读取属性文件的能力

这两个接口都是Spring自动帮我们调用其中的方法的。也就是说,RequestMappingHandlerMapping通过这些能力来完成初始化HandlerMapping接口的。

以上是对RequestMappingHandlerMapping的宏观分析,下面我们进行内部细节分析。

整体流程

一、通过实现ApplicationContextAware接口,完成拦截器相关组件的初始化

调用AbstractHandlerMapping类的initApplicationContext()方法。//TODO 下节分析

二、通过实现InitializingBean接口,完成url与处理器方法的映射(url->RequestMappingInfo,RequstMappingInfo->HandlerMehtod)

调用RequestMappingHandlerMapping类实现的afterPropertiesSet()方法,通过该方法最终调到其父类的initHandlerMethods()方法,这个方法是完成映射解析工作:

</>复制代码

  1. 1、获取上下文环境中所有的bean

  2. 2、迭代所有的bean,通过isHandler方法判断是否是handler

  3. </>复制代码

    1. 2.1 调用RequestMappingHandlerMapping.isHandler方法,根据@Controller或@RequestMapping注解判断(“或”关系,任意一个)
  4. 3、解析出handler中所有需要处理的方法,即标注了@RequestMapping注解的方法,封装在detectHandlerMehtods方法中

  5. </>复制代码

    1. 3.1 获取到原始的Class

    2. 3.2 使用MethodIntrospector.selectMethods方法过滤具体的handler method,通过模板方法模式getMappingForMethod预留给子类

    3. </>复制代码

      1. RequestMappingHandlerMapping.getMappingForMehtod方法,通过方法、类上面@RequestMapping注解生成匹配条件RequestMappingInfo对象

    4. 3.3 对过滤到的每个method进行注册,通过registerHandlerMehtod方法完成

    5. </>复制代码

      1. 通过createHandlerMethod方法创建HandlerMethod对象来封装处理器方法

      2. 判断匹配条件与处理器是否冲突,即同一个匹配条件只能对应一个处理器方法

      3. 把匹配条件与处理器方法存入map

      4. 从匹配条件中解析出url,通过RequestMappingInfoHandlerMapping.getMappingPathPatterns方法实现,之后把url与匹配条件存入另一个map

  6. 4、对HandlerMethod进行初始化,调用handlerMethodsInitialized方法,其内部什么都没做

以上是初始化RequestMappingHandlerMapping的整体流程。

源码分析

AbstractHandlerMapping

初始化拦截器

</>复制代码

  1. // 初始化拦截器,即初始化url需要的拦截器
  2. @Override
  3. protected void initApplicationContext() throws BeansException {
  4. extendInterceptors(this.interceptors);
  5. detectMappedInterceptors(this.adaptedInterceptors);
  6. initInterceptors();
  7. }

AbstractHandlerMethodMapping

控制初始化RequestMappingHandlerMapping.initHanderMethods方法整体流程,代码如下:

</>复制代码

  1. /**
  2. * 在ApplicationContext上下文扫描所有的bean,检测和注册处理器方法(handler method)
  3. */
  4. protected void initHandlerMethods() {
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Looking for request mappings in application context: " + getApplicationContext());
  7. }
  8. // 从上下文中查找所有的bean
  9. String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
  10. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
  11. obtainApplicationContext().getBeanNamesForType(Object.class));
  12. // 遍历所有的beanName
  13. for (String beanName : beanNames) {
  14. if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
  15. Class beanType = null;
  16. try {
  17. // 获得bean的类型
  18. beanType = obtainApplicationContext().getType(beanName);
  19. }
  20. catch (Throwable ex) {
  21. // An unresolvable bean type, probably from a lazy bean - let"s ignore it.
  22. if (logger.isDebugEnabled()) {
  23. logger.debug("Could not resolve target class for bean with name "" + beanName + """, ex);
  24. }
  25. }
  26. // 判断类是否被@Controller或是@RequestMapping注释
  27. // isHandler方法由子类RequestMappingHandlerMapping去实现
  28. if (beanType != null && isHandler(beanType)) {
  29. // 注册url与处理器方法的关系
  30. detectHandlerMethods(beanName);
  31. }
  32. }
  33. }
  34. // 空方法
  35. handlerMethodsInitialized(getHandlerMethods());
  36. }
  37. /**
  38. * 查找处理程序handler中的处理方法
  39. */
  40. protected void detectHandlerMethods(final Object handler) {
  41. // 获取handler的类型
  42. Class handlerType = (handler instanceof String ?
  43. obtainApplicationContext().getType((String) handler) : handler.getClass());
  44. if (handlerType != null) {
  45. // 获取给定类的用户定义类型,通常为给定类的类型,但在cglib生成子类的情况下,返回的是原始类型
  46. final Class userType = ClassUtils.getUserClass(handlerType);
  47. // 获取处理器方法map,key是Method,value是匹配条件RequestMappingInfo对象
  48. // map中不包括未被@RequestMapping注解的方法
  49. Map methods = MethodIntrospector.selectMethods(userType,
  50. (MethodIntrospector.MetadataLookup) method -> {
  51. try {
  52. // 调用子类RequestMappingHandlerMapping的getMappingForMethod方法进行处理,即根据RequestMapping注解信息创建匹配条件RequestMappingInfo对象
  53. return getMappingForMethod(method, userType);
  54. }
  55. catch (Throwable ex) {
  56. throw new IllegalStateException("Invalid mapping on handler class [" +
  57. userType.getName() + "]: " + method, ex);
  58. }
  59. });
  60. if (logger.isDebugEnabled()) {
  61. logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
  62. }
  63. // 遍历map,获得Method和RequestMappingInfo,注册他们
  64. methods.forEach((method, mapping) -> {
  65. Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
  66. // 调用本类registerHandlerMethod()方法
  67. registerHandlerMethod(handler, invocableMethod, mapping);
  68. });
  69. }
  70. }
  71. /**
  72. * 注册rul和处理器方法的映射关系
  73. */
  74. protected void registerHandlerMethod(Object handler, Method method, T mapping) {
  75. // 调用内部类MappingRegistry的register()方法
  76. this.mappingRegistry.register(mapping, handler, method);
  77. }
  78. /**
  79. * 此方法是内部类MappingRegistry的方法
  80. */
  81. public void register(T mapping, Object handler, Method method) {
  82. this.readWriteLock.writeLock().lock();
  83. try {
  84. // 创建处理器方法HandlerMethod实例,即Controller中的处理方法
  85. HandlerMethod handlerMethod = createHandlerMethod(handler, method);
  86. // 判断匹配条件是否重复,即一个@RequestMapping的映射url只能对应一个方法
  87. assertUniqueMethodMapping(handlerMethod, mapping);
  88. if (logger.isInfoEnabled()) {
  89. logger.info("Mapped "" + mapping + "" onto " + handlerMethod);
  90. }
  91. // 将匹配条件RequestMappingInfo和处理器方法保存到map中
  92. this.mappingLookup.put(mapping, handlerMethod);
  93. // 获得url映射路径,将映射路径和匹配条件对象RequestMappingInfo存起来
  94. // 调用本类的getDerectUrls方法
  95. List directUrls = getDirectUrls(mapping);
  96. for (String url : directUrls) {
  97. this.urlLookup.add(url, mapping);
  98. }
  99. String name = null;
  100. if (getNamingStrategy() != null) {
  101. name = getNamingStrategy().getName(handlerMethod, mapping);
  102. addMappingName(name, handlerMethod);
  103. }
  104. CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
  105. if (corsConfig != null) {
  106. this.corsLookup.put(handlerMethod, corsConfig);
  107. }
  108. // 将映射注册对象存入map
  109. this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
  110. }
  111. finally {
  112. this.readWriteLock.writeLock().unlock();
  113. }
  114. }
  115. private List getDirectUrls(T mapping) {
  116. List urls = new ArrayList<>(1);
  117. // 调用子类RequestMappingInfoHandlerMapping.getMappingPathPatterns方法
  118. for (String path : getMappingPathPatterns(mapping)) {
  119. if (!getPathMatcher().isPattern(path)) {
  120. urls.add(path);
  121. }
  122. }
  123. return urls;
  124. }

RequestMappingHandlerMapping

根据@RequestMapping生成RequestMappingInfo对象,主要代码如下:

</>复制代码

  1. /**
  2. * 使用方法和类型注解@RequestMapping创建RequestMappingInfo对象
  3. */
  4. @Override
  5. @Nullable
  6. protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) {
  7. // 创建方法的RequestMappingInfo
  8. RequestMappingInfo info = createRequestMappingInfo(method);
  9. if (info != null) {
  10. // 创建类的RequestMappingInfo
  11. RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
  12. if (typeInfo != null) {
  13. // 将方法RequestMappingInfo和类RequestMappingInfo合并,比如Controller类上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),结果为"/demo/demo1"
  14. info = typeInfo.combine(info);
  15. }
  16. }
  17. return info;
  18. }
  19. @Nullable
  20. private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
  21. // 获取RequestMapping注解
  22. RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
  23. RequestCondition condition = (element instanceof Class ?
  24. getCustomTypeCondition((Class) element) : getCustomMethodCondition((Method) element));
  25. // 调用createRequestMappingInfo创建匹配条件对象
  26. return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
  27. }
  28. /**
  29. * 构造匹配条件对象
  30. */
  31. protected RequestMappingInfo createRequestMappingInfo(
  32. RequestMapping requestMapping, @Nullable RequestCondition customCondition) {
  33. RequestMappingInfo.Builder builder = RequestMappingInfo
  34. .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
  35. .methods(requestMapping.method())
  36. .params(requestMapping.params())
  37. .headers(requestMapping.headers())
  38. .consumes(requestMapping.consumes())
  39. .produces(requestMapping.produces())
  40. .mappingName(requestMapping.name());
  41. if (customCondition != null) {
  42. builder.customCondition(customCondition);
  43. }
  44. return builder.options(this.config).build();
  45. }

RequestMappingInfoHandlerMapping

提供匹配条件RequestMappingInfo的解析处理,涉及的代码如下:

</>复制代码

  1. /**
  2. * 获取url集合,即@RequestMapping中设置的valuepath
  3. */
  4. @Override
  5. protected Set getMappingPathPatterns(RequestMappingInfo info) {
  6. return info.getPatternsCondition().getPatterns();
  7. }

以上即RequestMappingHandlerMapping对象的初始化过程及初始化过程的核心源码。

总结

本文主要分析了RequestMappingHandlerMapping的初始化过程,希望对大家有帮助。随着学习的深入,后面有时间在分析下期中涉及的关键bean,比如:RequestMappingInfo、RequestCondition、HandlerMethod等等。大家加油!

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69487.html

相关文章

  • SpringMVC源码分析--HandlerMapping(一)

    摘要:接口接口作用是将请求映射到处理程序,以及预处理和处理后的拦截器列表,映射是基于一些标准的,其中的细节因不同的实现而不相同。该参数是类型,作用是检查所有的映射解析器或使用或为的,默认为,即从上下文中检查所有的。 概述 在Spring MVC启动章节https://segmentfault.com/a/1190000014674239,介绍到了DispatcherServlet的onRef...

    ralap 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(五)

    摘要:概述通过前三章的分析,我们简要分析了和,但对拦截器部分做详细的分析,拦截器的加载和初始化是三个相同的部分。 概述 通过前三章的分析,我们简要分析了SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,但对拦截器部分做详细的分析,拦截器的加载和初始化是三个HandlerMapping相...

    nanchen2251 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(三)

    摘要:与类图对比,类继承自抽象类,其又继承自抽象类,再往上继承关系与一致。创建初始化上一章我们分析了的创建初始化过程,的创建初始化过程与一样,方法的入口在抽象类中的方法。至此,代码编写完毕。 概述 本节我们继续分析HandlerMapping另一个实现类BeanNameUrlHandlerMapping,从类的名字可知,该类会根据请求的url与spring容器中定义的bean的name属性值...

    fsmStudy 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(六)

    摘要:概述上一节我们分析了的初始化过程,即创建并注册,本章我们分析下的请求处理过程,即查找。本系列文章是基于。最后创建了群方便大家交流,可扫描加入,同时也可加我,共同学习共同进步,谢谢 概述 上一节我们分析了RequestMappingHandlerMapping的初始化过程,即创建并注册HandlerMehtod,本章我们分析下RequestMappingHandlerMapping的请求...

    BDEEFE 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(二)

    摘要:由于抽象类重写了父类的方法,所以此时会调用的方法,在该方法中通过调用父类的方法,该方法通过模板方法模式最终调到类的方法。分析该类间接实现了接口,直接实现该接口的是抽象类,映射与请求。 概述 在前一章https://segmentfault.com/a/1190000014901736的基础上继续分析,主要完成SimpleUrlHandlerMapping类的原理。 本系列文章是基于Sp...

    Imfan 评论0 收藏0

发表评论

0条评论

imccl

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<