当前位置:   article > 正文

SpringMVC源码解析(一)——初始化

spring mvc 怎么判定触发源

前言

    本系列文章顺延“Spring源码解析”,是在“父容器”创建完成后,对“子容器”(SpringMVC)创建,以及请求处理的解析。

 

源码解读

    说起 SpringMVC,DispatcherServlet 应该是最熟悉的类之一。它几乎掌控着整个请求的分发以及最终响应,我们就从它来追溯“子容器”创建的源头。

  1. public abstract class GenericServlet implements Servlet, ServletConfig,
  2. java.io.Serializable {
  3. // getInitParameter可获取配置的 <init-param>
  4. // getServletName可获取配置的 <servlet-name>
  5. private transient ServletConfig config;
  6. // SpringMVC启动入口
  7. @Override
  8. public void init(ServletConfig config) throws ServletException {
  9. this.config = config;
  10. // 子类实现
  11. this.init();
  12. }
  13. }

    该类就是 DispatcherServlet 的最顶层抽象父类了,它实现了 Servlet.init 方法,通过 Servlet 规范我们可以了解到,根据 <load-on-startup> 配置的不同,调用时机也不同。

  • ≥0 :会在 Servlet 被实例化后执行;
  • <0:在该 Servlet 被第一次请求时才调用,不填默认走该逻辑
  1. public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
  2. // GenericServlet.init调用
  3. @Override
  4. public final void init() throws ServletException {
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Initializing servlet '" + getServletName() + "'");
  7. }
  8. // 将 <init-param>键值对封装成 PropertyValue
  9. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  10. if (!pvs.isEmpty()) {
  11. try {
  12. // 将自身(DispatcherServlet)封装成 BeanWrapper,方便 Spring注入
  13. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  14. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  15. // 注册解析器,对于 Resource类型的属性用 ResourceEditor解析
  16. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  17. initBeanWrapper(bw);
  18. // 属性注入:第二个参数会忽略未找到的属性
  19. bw.setPropertyValues(pvs, true);
  20. } catch (BeansException ex) {
  21. if (logger.isErrorEnabled()) {
  22. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  23. }
  24. throw ex;
  25. }
  26. }
  27. // 子类扩展实现
  28. initServletBean();
  29. if (logger.isDebugEnabled()) {
  30. logger.debug("Servlet '" + getServletName() + "' configured successfully");
  31. }
  32. }
  33. }

    这一层主要是针对配置属性相关的代码,如果看过之前“实例创建”章节,对 bw.setPropertyValues(pvs, true) 应该不陌生,就是为 Servlet 做属性填充。接下来 initServletBean 方法实现了几乎“子容器”创建的大部分工作,由 FrameworkServlet 实现:

  1. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
  2. // 可用 <init-param>:contextClass指定
  3. private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
  4. // 默认 XmlWebApplicationContext
  5. public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
  6. @Override
  7. protected final void initServletBean() throws ServletException {
  8. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
  9. if (this.logger.isInfoEnabled()) {
  10. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
  11. }
  12. long startTime = System.currentTimeMillis();
  13. try {
  14. // 初始化容器
  15. this.webApplicationContext = initWebApplicationContext();
  16. // 空实现:可自定义扩展
  17. initFrameworkServlet();
  18. }....// 省略 catch
  19. ....// 省略日志
  20. }
  21. protected WebApplicationContext initWebApplicationContext() {
  22. // 获取父容器:通过调用 ServletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)
  23. // 设置逻辑见容器启动: ContextLoader.initWebApplicationContext
  24. WebApplicationContext rootContext =
  25. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  26. WebApplicationContext wac = null;
  27. // 是通过构造器传入的方式会走这个分支(见 SpringBoot)
  28. if (this.webApplicationContext != null) {
  29. wac = this.webApplicationContext;
  30. if (wac instanceof ConfigurableWebApplicationContext) {
  31. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  32. // 调用 refresh会置 active为 true
  33. if (!cwac.isActive()) {
  34. if (cwac.getParent() == null) {
  35. // 设置父容器
  36. cwac.setParent(rootContext);
  37. }
  38. configureAndRefreshWebApplicationContext(cwac);
  39. }
  40. }
  41. }
  42. if (wac == null) {
  43. // 查找 contextAttribute是否已绑定的上下文 key
  44. wac = findWebApplicationContext();
  45. }
  46. if (wac == null) {
  47. // 如果还没有找到上下文,创建一个
  48. wac = createWebApplicationContext(rootContext);
  49. }
  50. if (!this.refreshEventReceived) {
  51. // 子容器(SpringMVC),在容器刷新完毕后执行
  52. onRefresh(wac);
  53. }
  54. if (this.publishContext) {
  55. String attrName = getServletContextAttributeName();
  56. getServletContext().setAttribute(attrName, wac);
  57. if (this.logger.isDebugEnabled()) {
  58. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
  59. "' as ServletContext attribute with name [" + attrName + "]");
  60. }
  61. }
  62. return wac;
  63. }
  64. protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
  65. return createWebApplicationContext((ApplicationContext) parent);
  66. }
  67. protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
  68. // 默认 XmlWebApplicationContext
  69. // 可用<init-param>:contextClass指定
  70. Class<?> contextClass = getContextClass();
  71. if (this.logger.isDebugEnabled()) {
  72. this.logger.debug("Servlet with name '" + getServletName() +
  73. "' will try to create custom WebApplicationContext context of class '" +
  74. contextClass.getName() + "'" + ", using parent context [" + parent + "]");
  75. }
  76. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  77. throw new ApplicationContextException(
  78. "Fatal initialization error in servlet with name '" + getServletName() +
  79. "': custom WebApplicationContext class [" + contextClass.getName() +
  80. "] is not of type ConfigurableWebApplicationContext");
  81. }
  82. // 使用反射调用构造器实例化
  83. ConfigurableWebApplicationContext wac =
  84. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  85. wac.setEnvironment(getEnvironment());
  86. // 设置父容器
  87. wac.setParent(parent);
  88. // 获取 <servlet>中配置的 <init-param>:contextConfigLocation
  89. String configLocation = getContextConfigLocation();
  90. if (configLocation != null) {
  91. // 设置“mvc”配置文件所在路径
  92. wac.setConfigLocation(configLocation);
  93. }
  94. // 和父容器相似的套路
  95. configureAndRefreshWebApplicationContext(wac);
  96. return wac;
  97. }
  98. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
  99. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  100. // 设置 ID
  101. if (this.contextId != null) {
  102. wac.setId(this.contextId);
  103. } else {
  104. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  105. ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
  106. }
  107. }
  108. // Servelt相关的设置
  109. wac.setServletContext(getServletContext());
  110. wac.setServletConfig(getServletConfig());
  111. wac.setNamespace(getNamespace());
  112. // 注册事件监听器:被 SourceFilteringListener包裹,仅在“子容器”(mvc)中触发
  113. // ContextRefreshListener:触发 FrameworkServlet.onApplicationEvent
  114. // onApplicationEvent进而调用:onRefresh
  115. wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
  116. // 任何情况下 initPropertySources都在上下文刷新时被调用
  117. ConfigurableEnvironment env = wac.getEnvironment();
  118. if (env instanceof ConfigurableWebEnvironment) {
  119. ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  120. }
  121. // 空实现
  122. postProcessWebApplicationContext(wac);
  123. // ApplicationContextInitializer执行,由以下属性指定
  124. // <init-param>:globalInitializerClasses、contextInitializerClasses指定
  125. applyInitializers(wac);
  126. // 容器刷新:AbstractApplicationContext实现(同父容器)
  127. wac.refresh();
  128. }
  129. }

    抛开一些设置属性的步骤不谈,上面的逻辑主要可以总结为:容器实例化(反射调用构造器)——>将<init-param>指定的“mvc”配置文件设置到容器,以便随后的解析——>然后注册了一个“容器刷新”事件监听器,以便回调——>调用 AbstractApplicationContext.refresh 刷新容器——>最后回调 onRefresh

    这里的 onRefresh 在 FrameworkServlet 中是空实现,由子类定制实现。来看看 DispatcherServlet 的实现吧。

  1. public class DispatcherServlet extends FrameworkServlet {
  2. // 在 refresh中的 finishRefresh中会发布“刷新”事件
  3. // 会触发 ContextRefreshListener监听器,继而调用该方法
  4. @Override
  5. protected void onRefresh(ApplicationContext context) {
  6. initStrategies(context);
  7. }
  8. // 主要初始化了 mvc中所必须的成员变量
  9. protected void initStrategies(ApplicationContext context) {
  10. // 如果定义了 MultipartResolver,实例化
  11. initMultipartResolver(context);
  12. // 如果定义了 LocaleResolver,实例化
  13. // 默认使用 AcceptHeaderLocaleResolver
  14. initLocaleResolver(context);
  15. // 如果定义了 ThemeResolver,实例化
  16. // 默认使用 FixedThemeResolver
  17. initThemeResolver(context);
  18. // 初始化所有的 HandlerMapping:具体处理器
  19. // 初始化参数 detectAllHandlerMappings设为 false可以只加载指定的bean
  20. initHandlerMappings(context);
  21. // 初始化所有的 HandlerAdapter
  22. // 初始化参数 detectAllHandlerAdapters设为 false可以只加载指定的bean
  23. initHandlerAdapters(context);
  24. // 初始化所有的 HandlerExceptionResolver(异常处理)
  25. initHandlerExceptionResolvers(context);
  26. // 如果定义了 RequestToViewNameTranslator,实例化
  27. // 默认使用 DefaultRequestToViewNameTranslator
  28. initRequestToViewNameTranslator(context);
  29. // 初始化所有的 ViewResolver
  30. // 默认使用 InternalResourceViewResolver
  31. initViewResolvers(context);
  32. // 如果定义了 FlashMapManager,实例化
  33. // 默认使用 DefaultFlashMapManager
  34. initFlashMapManager(context);
  35. }
  36. }

    以上的这些就是“子容器”和“父容器”差异之处了,支持了“mvc”框架例如文件上传请求映射异常处理视图解析等功能。我们会在之后的章节展开讲解。

 

总结

    本篇主要梳理了“子容器”(SpringMVC)的初始化脉络,顶层父类留下可扩展的“口子”,让子类去实现具体的初始化逻辑。

转载于:https://my.oschina.net/marvelcode/blog/1835589

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/1015710
推荐阅读
相关标签
  

闽ICP备14008679号