SpringMVC启动流程源码分析

た 入场券 2022-11-15 12:54 351阅读 0赞

Spring MVC向WEB容器中注入了两个对象:

  • ContextLoaderListener:由AbstractContextLoaderInitializer注入。
  • DispatcherServlet:由AbstractDispatcherServletInitializer注入。

下面分别分析这两个对象在WEB容器启动时做了什么工作?

ContextLoaderListener

ContextLoaderListener实现了Servlet规范中的javax.servlet.ServletContextListener,WEB容器在启动过程中会回调ServletContextListener.contextInitialized(),所以ContextLoaderListener类需要重点关注contextInitialized()。

先来看看ContextLoaderListener是怎么被加入到ServletContext中的:

org.springframework.web.context.AbstractContextLoaderInitializer#registerContextLoaderListener

  1. protected void registerContextLoaderListener(ServletContext servletContext) {
  2. // 创建父容器
  3. WebApplicationContext rootAppContext = createRootApplicationContext();
  4. if (rootAppContext != null) {
  5. // 创建ContextLoaderListener
  6. ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
  7. listener.setContextInitializers(getRootApplicationContextInitializers());
  8. servletContext.addListener(listener);
  9. }
  10. else {
  11. logger.debug("No ContextLoaderListener registered, as " +
  12. "createRootApplicationContext() did not return an application context");
  13. }
  14. }

也就是在创建ContextLoaderListener时,父容器已经创建完成,注意只是创建完成,并没有调用refresh()方法完成初始化。

org.springframework.web.context.ContextLoaderListener#contextInitialized

  1. public void contextInitialized(ServletContextEvent event) {
  2. initWebApplicationContext(event.getServletContext());
  3. }

org.springframework.web.context.ContextLoader#initWebApplicationContext

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  2. /** * 首先通过WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个String类型的静态变量获取一个父容器 * 父容器作为全局变量存储在application对象中,如果存在则有且只能有一个 * 如果在初始化父容器时发现已经存在则直接抛出异常 * 这个值在下面的代码中会放入application中 */
  3. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  4. throw new IllegalStateException(
  5. "Cannot initialize context because there is already a root application context present - " +
  6. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  7. }
  8. servletContext.log("Initializing Spring root WebApplicationContext");
  9. Log logger = LogFactory.getLog(ContextLoader.class);
  10. if (logger.isInfoEnabled()) {
  11. logger.info("Root WebApplicationContext: initialization started");
  12. }
  13. long startTime = System.currentTimeMillis();
  14. try {
  15. // Store context in local instance variable, to guarantee that
  16. // it is available on ServletContext shutdown.
  17. // context代表父容器,已经有值了
  18. if (this.context == null) {
  19. this.context = createWebApplicationContext(servletContext);
  20. }
  21. if (this.context instanceof ConfigurableWebApplicationContext) {
  22. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  23. if (!cwac.isActive()) {
  24. // The context has not yet been refreshed -> provide services such as
  25. // setting the parent context, setting the application context id, etc
  26. if (cwac.getParent() == null) {
  27. // The context instance was injected without an explicit parent ->
  28. // determine parent for root web application context, if any.
  29. ApplicationContext parent = loadParentContext(servletContext); // null
  30. cwac.setParent(parent);
  31. }
  32. // 调用refresh()方法完成父容器的初始化
  33. configureAndRefreshWebApplicationContext(cwac, servletContext);
  34. }
  35. }
  36. // 将父容器放入到了servletContext中
  37. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  38. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  39. if (ccl == ContextLoader.class.getClassLoader()) {
  40. currentContext = this.context;
  41. }
  42. else if (ccl != null) {
  43. currentContextPerThread.put(ccl, this.context);
  44. }
  45. if (logger.isInfoEnabled()) {
  46. long elapsedTime = System.currentTimeMillis() - startTime;
  47. logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
  48. }
  49. return this.context;
  50. }
  51. catch (RuntimeException | Error ex) {
  52. logger.error("Context initialization failed", ex);
  53. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  54. throw ex;
  55. }
  56. }

总结:ContextLoaderListener主要负责父容器的初始化。

DispatcherServlet

先来看下DispatcherServlet的创建:

org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#onStartup

  1. public void onStartup(ServletContext servletContext) throws ServletException {
  2. super.onStartup(servletContext); // 创建ContextLoaderListener
  3. registerDispatcherServlet(servletContext); // 创建DispatcherServlet
  4. }
  5. protected void registerDispatcherServlet(ServletContext servletContext) {
  6. String servletName = getServletName();
  7. Assert.hasLength(servletName, "getServletName() must not return null or empty");
  8. WebApplicationContext servletAppContext = createServletApplicationContext(); // 子容器
  9. Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
  10. // 创建DispatcherServlet
  11. FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
  12. Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
  13. dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); // null
  14. ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
  15. if (registration == null) {
  16. throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
  17. "Check if there is another servlet registered under the same name.");
  18. }
  19. registration.setLoadOnStartup(1);
  20. registration.addMapping(getServletMappings()); // 处理哪些映射
  21. registration.setAsyncSupported(isAsyncSupported()); // 默认为true,支持异步
  22. Filter[] filters = getServletFilters(); // 过滤器
  23. if (!ObjectUtils.isEmpty(filters)) {
  24. for (Filter filter : filters) {
  25. // 添加filter到servlet容器中
  26. registerServletFilter(servletContext, filter);
  27. }
  28. }
  29. customizeRegistration(registration);
  30. }
  31. protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
  32. return new DispatcherServlet(servletAppContext);
  33. }

先创建子容器,然后将子容器作为DispatcherServlet的构造方法的参数传入,注意这个子容器只是刚刚被创建,并没有调用refresh()进行初始化,那么什么时候被初始化呢?

看一下DispatcherServlet的继承关系:

在这里插入图片描述

WEB容器启动后会调用Servlet的init()方法进行初始化,此方法的实现实在父类HttpServletBean中,源码如下:

org.springframework.web.servlet.HttpServletBean#init

  1. public final void init() throws ServletException {
  2. // Set bean properties from init parameters.
  3. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  4. if (!pvs.isEmpty()) {
  5. try {
  6. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  7. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  8. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  9. initBeanWrapper(bw);
  10. bw.setPropertyValues(pvs, true);
  11. }
  12. catch (BeansException ex) {
  13. if (logger.isErrorEnabled()) {
  14. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  15. }
  16. throw ex;
  17. }
  18. }
  19. // Let subclasses do whatever initialization they like.
  20. // 留给子类实现
  21. initServletBean();
  22. }

org.springframework.web.servlet.FrameworkServlet#initServletBean

  1. protected final void initServletBean() throws ServletException {
  2. getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
  3. if (logger.isInfoEnabled()) {
  4. logger.info("Initializing Servlet '" + getServletName() + "'");
  5. }
  6. long startTime = System.currentTimeMillis();
  7. try {
  8. // 对子容器进行初始化
  9. this.webApplicationContext = initWebApplicationContext();
  10. initFrameworkServlet(); // NOP
  11. }
  12. catch (ServletException | RuntimeException ex) {
  13. logger.error("Context initialization failed", ex);
  14. throw ex;
  15. }
  16. if (logger.isDebugEnabled()) {
  17. String value = this.enableLoggingRequestDetails ?
  18. "shown which may lead to unsafe logging of potentially sensitive data" :
  19. "masked to prevent unsafe logging of potentially sensitive data";
  20. logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
  21. "': request parameters and headers will be " + value);
  22. }
  23. if (logger.isInfoEnabled()) {
  24. logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
  25. }
  26. }

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

  1. protected WebApplicationContext initWebApplicationContext() {
  2. // 从ServletContext中获取父容器
  3. WebApplicationContext rootContext =
  4. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  5. WebApplicationContext wac = null;
  6. if (this.webApplicationContext != null) {
  7. wac = this.webApplicationContext; // 子容器
  8. if (wac instanceof ConfigurableWebApplicationContext) {
  9. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  10. if (!cwac.isActive()) {
  11. if (cwac.getParent() == null) {
  12. cwac.setParent(rootContext); // 父子间建立关系
  13. }
  14. // 调用refresh()方法对子容器进行初始化
  15. configureAndRefreshWebApplicationContext(cwac);
  16. }
  17. }
  18. }
  19. if (wac == null) { // 不会进入
  20. wac = findWebApplicationContext();
  21. }
  22. if (wac == null) { // 不会进入
  23. wac = createWebApplicationContext(rootContext);
  24. }
  25. if (!this.refreshEventReceived) { // refreshEventReceived=true不会进入
  26. synchronized (this.onRefreshMonitor) {
  27. onRefresh(wac);
  28. }
  29. }
  30. if (this.publishContext) {
  31. String attrName = getServletContextAttributeName();
  32. // 将子容器也放入到ServletContext中
  33. getServletContext().setAttribute(attrName, wac);
  34. }
  35. return wac;
  36. }

org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
  2. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  3. // The application context id is still set to its original default value
  4. // -> assign a more useful id based on available information
  5. if (this.contextId != null) {
  6. wac.setId(this.contextId);
  7. }
  8. else {
  9. // Generate default id...
  10. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  11. ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
  12. }
  13. }
  14. wac.setServletContext(getServletContext());
  15. wac.setServletConfig(getServletConfig());
  16. wac.setNamespace(getNamespace());
  17. // 添加了一个监听子容器刷新的事件
  18. wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
  19. // The wac environment's #initPropertySources will be called in any case when the context
  20. // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  21. // use in any post-processing or initialization that occurs below prior to #refresh
  22. ConfigurableEnvironment env = wac.getEnvironment();
  23. if (env instanceof ConfigurableWebEnvironment) {
  24. ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  25. }
  26. postProcessWebApplicationContext(wac); // NOP
  27. applyInitializers(wac);
  28. // invoke refresh method
  29. wac.refresh();
  30. }

在子容器初始化之前添加了一个监听容器刷新的事件ContextRefreshListener,当容器刷新完成后将会调用ContextRefreshListener.onApplicationEvent()方法。

从泛型来看监听的是容器刷新事件ContextRefreshedEvent,然后又会调用FrameworkServlet.onApplicationEvent()。

  1. private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
  2. @Override
  3. public void onApplicationEvent(ContextRefreshedEvent event) {
  4. FrameworkServlet.this.onApplicationEvent(event);
  5. }
  6. }

org.springframework.web.servlet.FrameworkServlet#onApplicationEvent

  1. public void onApplicationEvent(ContextRefreshedEvent event) {
  2. this.refreshEventReceived = true;
  3. synchronized (this.onRefreshMonitor) {
  4. onRefresh(event.getApplicationContext());
  5. }
  6. }

org.springframework.web.servlet.DispatcherServlet#onRefresh

  1. protected void onRefresh(ApplicationContext context) {
  2. initStrategies(context);
  3. }
  4. protected void initStrategies(ApplicationContext context) {
  5. // 这里的各种类都会使用默认的
  6. // 默认的配置位于spring-webmvc org/springframework/web/servlet/DispatcherServlet.properties
  7. initMultipartResolver(context); // null
  8. initLocaleResolver(context); // AcceptHeaderLocaleResolver
  9. initThemeResolver(context); // FixedThemeResolver
  10. initHandlerMappings(context); // BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping
  11. initHandlerAdapters(context); // HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter、HandlerFunctionAdapter
  12. initHandlerExceptionResolvers(context); // ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
  13. initRequestToViewNameTranslator(context); // DefaultRequestToViewNameTranslator
  14. initViewResolvers(context); // InternalResourceViewResolver
  15. initFlashMapManager(context); // SessionFlashMapManager
  16. }

DispatcherServlet.properties
的内容如下:

  1. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
  2. org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
  3. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
  4. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
  5. org.springframework.web.servlet.function.support.RouterFunctionMapping
  6. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
  7. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
  8. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
  9. org.springframework.web.servlet.function.support.HandlerFunctionAdapter
  10. org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
  11. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
  12. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
  13. org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
  14. org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
  15. org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

onRefresh()方法用于初始化SpringMVC开发中的九大组件:

  1. 文件上传解析器
  2. 本地化解析器
  3. 主题解析器
  4. HandlerMapping处理器映射器
  5. HandlerAdapter处理器适配器
  6. 异常解析器
  7. 请求到视图名称的转换器
  8. 视图解析器
  9. flashMap管理器

发表评论

表情:
评论列表 (有 0 条评论,351人围观)

还没有评论,来说两句吧...

相关阅读