资讯专栏INFORMATION COLUMN

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

Imfan / 1738人阅读

摘要:由于抽象类重写了父类的方法,所以此时会调用的方法,在该方法中通过调用父类的方法,该方法通过模板方法模式最终调到类的方法。分析该类间接实现了接口,直接实现该接口的是抽象类,映射与请求。

概述

在前一章https://segmentfault.com/a/1190000014901736的基础上继续分析,主要完成SimpleUrlHandlerMapping类的原理。

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

类图

在分析类之前,先了解下类的继承关系,如下图:

红框的类就是我们本章要分析的类。

创建/初始化

从类图关系上可以看出,SimpleUrlHanderMapping类最终实现了ApplicationContextAware接口,该接口定义了方法setApplicationContext(applicationContext),其作用是实现该接口的类,在Spring实例化类时,自动调用setApplicationContext(applicationContext)方法。

ApplicationObjectSupport抽象类实现了ApplicationContextAware接口的setApplicationContext(applicationContext)方法。

由于WebApplicationObjectSupport抽象类重写了父类ApplicationObjectSupport的initApplicationContext(context)方法,所以此时会调用WebApplicationObjectSupport的initAppliationContext(context)方法,在该方法中通过suppr调用父类的initApplicationContext(context)方法,该方法通过模板方法模式最终调到SimpleUrlHandlerMapping类的initApplicationContext()方法。

整个创建涉及的类比较多,过程比较复杂,文字描述也很乏味,所以我画了调用时序图,可供大家参考:

分析到此,我们就找到了SimpleUrlHandlerMapping类的入口方法,即本类的initApplicationContext()方法。

分析

SimpleUrlHandlerMapping

该类间接实现了org.springframework.web.servlet.HandlerMapping接口,直接实现该接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象类,映射Url与请求handler bean。支持映射bean实例和映射bean名称。源代码如下:

</>复制代码

  1. public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
  2. // 存储url和bean映射
  3. private final Map urlMap = new LinkedHashMap<>();
  4. // 注入property的name为mappings映射
  5. public void setMappings(Properties mappings) {
  6. CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
  7. }
  8. // 注入property的name为urlMap映射
  9. public void setUrlMap(Map urlMap) {
  10. this.urlMap.putAll(urlMap);
  11. }
  12. public Map getUrlMap() {
  13. return this.urlMap;
  14. }
  15. // 实例化本类实例入口
  16. @Override
  17. public void initApplicationContext() throws BeansException {
  18. // 调用父类AbstractHandlerMapping的initApplicationContext方法,只要完成拦截器的注册
  19. super.initApplicationContext();
  20. // 处理url和bean name,具体注册调用父类完成
  21. registerHandlers(this.urlMap);
  22. }
  23. // 注册映射关系,及将property中的值解析到map对象中,key为url,value为bean id或name
  24. protected void registerHandlers(Map urlMap) throws BeansException {
  25. if (urlMap.isEmpty()) {
  26. logger.warn("Neither "urlMap" nor "mappings" set on SimpleUrlHandlerMapping");
  27. }
  28. else {
  29. urlMap.forEach((url, handler) -> {
  30. // 增加以"/"开头
  31. if (!url.startsWith("/")) {
  32. url = "/" + url;
  33. }
  34. // 去除handler bean名称的空格
  35. if (handler instanceof String) {
  36. handler = ((String) handler).trim();
  37. }
  38. // 调用父类AbstractUrlHandlerMapping完成映射
  39. registerHandler(url, handler);
  40. });
  41. }
  42. }
  43. }

从以上代码可知,SimpleUrlHandlerMapping类主要接收用户设定的url与handler的映射关系,其实际的工作都是交由其父类来完成的。

AbstractHandlerMapping

在创建初始化SimpleUrlHandlerMapping类时,调用其父类的initApplicationContext()方法,该方法完成拦截器的初始化,代码如下:

</>复制代码

  1. @Override
  2. protected void initApplicationContext() throws BeansException {
  3. // 空实现。
  4. // 子类可重写此方法以注册额外的拦截器
  5. extendInterceptors(this.interceptors);
  6. // 从上下文中查询拦截器并添加到拦截器列表中
  7. detectMappedInterceptors(this.adaptedInterceptors);
  8. // 初始化拦截器
  9. initInterceptors();
  10. }
  11. // 查找实现了MappedInterceptor接口的bean,并添加到映射拦截器列表
  12. protected void detectMappedInterceptors(List mappedInterceptors) {
  13. mappedInterceptors.addAll(
  14. BeanFactoryUtils.beansOfTypeIncludingAncestors(
  15. obtainApplicationContext(), MappedInterceptor.class, true, false).values());
  16. }
  17. // 将自定义bean设置到适配拦截器中,bean需实现HandlerInterceptor或WebRequestInterceptor
  18. protected void initInterceptors() {
  19. if (!this.interceptors.isEmpty()) {
  20. for (int i = 0; i < this.interceptors.size(); i++) {
  21. Object interceptor = this.interceptors.get(i);
  22. if (interceptor == null) {
  23. throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
  24. }
  25. this.adaptedInterceptors.add(adaptInterceptor(interceptor));
  26. }
  27. }
  28. }

AbstractUrlHandlerMapping

在创建初始化SimpleUrlHandlerMapping类时,调用AbstractUrlHandlerMapping类的registerHandler(urlPath,handler)方法,该方法源码如下:

</>复制代码

  1. protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
  2. Assert.notNull(urlPath, "URL path must not be null");
  3. Assert.notNull(handler, "Handler object must not be null");
  4. Object resolvedHandler = handler;
  5. // 不是懒加载,默认为false,即不是,通过配置SimpleUrlHandlerMapping属性lazyInitHandlers的值进行控制
  6. // 如果不是懒加载并且handler为单例,即从上下文中查询实例处理,此时resolvedHandler为handler实例对象;
  7. // 如果是懒加载或者handler不是单例,即resolvedHandler为handler逻辑名
  8. if (!this.lazyInitHandlers && handler instanceof String) {
  9. String handlerName = (String) handler;
  10. ApplicationContext applicationContext = obtainApplicationContext();
  11. // 如果handler是单例,通过bean的scope控制
  12. if (applicationContext.isSingleton(handlerName)) {
  13. resolvedHandler = applicationContext.getBean(handlerName);
  14. }
  15. }
  16. Object mappedHandler = this.handlerMap.get(urlPath);
  17. if (mappedHandler != null) {
  18. if (mappedHandler != resolvedHandler) {
  19. throw new IllegalStateException(
  20. "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
  21. "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
  22. }
  23. }
  24. else {
  25. if (urlPath.equals("/")) {
  26. if (logger.isInfoEnabled()) {
  27. logger.info("Root mapping to " + getHandlerDescription(handler));
  28. }
  29. setRootHandler(resolvedHandler);
  30. }
  31. else if (urlPath.equals("/*")) {
  32. if (logger.isInfoEnabled()) {
  33. logger.info("Default mapping to " + getHandlerDescription(handler));
  34. }
  35. setDefaultHandler(resolvedHandler);
  36. }
  37. else {
  38. // 把url与handler(名称或实例)放入map,以供后续使用
  39. this.handlerMap.put(urlPath, resolvedHandler);
  40. if (logger.isInfoEnabled()) {
  41. logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
  42. }
  43. }
  44. }
  45. }

到此,SimpleUrlHandlerMapping类在容器启动期间的初始化完成。

总结

本文分析了SimpleUrlHandlerMapping类初始化过程,其实核心就是把url和handler进行了映射,供后续访问使用,单靠看文章无法掌握。整个过程调用很复杂,大家多debug跟踪,一定能了解其内部的逻辑。大家共勉!

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

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

转载请注明本文地址:https://www.ucloud.cn/yun/69466.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(四)

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

    imccl 评论0 收藏0

发表评论

0条评论

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