前两篇(Spring MVC源码——Root WebApplicationContext 和 Spring MVC源码——Servlet WebApplicationContext)讲述了springmvc项目创建上下文的过程,这一篇带大家了解一下springboot项目创建上下文的过程。

SpringApplication引导类

SpringApplication类用于启动或者引导springboot项目,直接应用在java main方法中。

  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2. this.resourceLoader = resourceLoader;
  3. Assert.notNull(primarySources, "PrimarySources must not be null");
  4. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  5. //判断当前web应用程序类型
  6. this.webApplicationType = deduceWebApplicationType();
  7. //找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化
  8. setInitializers((Collection) getSpringFactoriesInstances(
  9. ApplicationContextInitializer.class));
  10. //找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化
  11. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  12. //获得当前执行main方法的类对象
  13. this.mainApplicationClass = deduceMainApplicationClass();
  14. }

springboot项目WebApplicationType分为三种:非web类型,web类型(spring-mvc),响应式web类型(spring-webflux)

  1. private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
  2. "org.springframework.web.context.ConfigurableWebApplicationContext" };
  3.  
  4. private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
  5. + "web.reactive.DispatcherHandler";
  6.  
  7. private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
  8. + "web.servlet.DispatcherServlet";
  9.  
  10. private WebApplicationType deduceWebApplicationType() {
  11. if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
  12. && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
  13. return WebApplicationType.REACTIVE;
  14. }
  15. for (String className : WEB_ENVIRONMENT_CLASSES) {
  16. if (!ClassUtils.isPresent(className, null)) {
  17. return WebApplicationType.NONE;
  18. }
  19. }
  20. return WebApplicationType.SERVLET;
  21. }

下面的run方法是springboot项目启动的核心代码。

  1. public ConfigurableApplicationContext run(String... args) {
  2. //开启任务执行时间监听器
  3. StopWatch stopWatch = new StopWatch();
  4. stopWatch.start();
  5.  
  6. ConfigurableApplicationContext context = null;
  7. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  8.  
  9. //设置系统属性『java.awt.headless』,为true则启用headless模式支持
  10. configureHeadlessProperty();
  11.  
  12. //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
  13. //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
  14. //之后逐个调用其started()方法,广播SpringBoot要开始执行了。
  15. SpringApplicationRunListeners listeners = getRunListeners(args);
  16. listeners.starting();
  17. try {
  18. ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  19. args);
  20.  
  21. //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
  22. //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
  23. ConfigurableEnvironment environment = prepareEnvironment(listeners,
  24. applicationArguments);
  25. //是否搜索BeanInfo类
  26. configureIgnoreBeanInfo(environment);
  27. //Banner打印
  28. Banner printedBanner = printBanner(environment);
  29.  
  30. //根据WebApplicationType的值来决定创建何种类型的ApplicationContext对象
  31. context = createApplicationContext();
  32.  
  33. //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
  34. exceptionReporters = getSpringFactoriesInstances(
  35. SpringBootExceptionReporter.class,
  36. new Class[] { ConfigurableApplicationContext.class }, context);
  37.  
  38. //为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
  39. //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
  40. //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
  41. //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
  42. prepareContext(context, environment, listeners, applicationArguments,
  43. printedBanner);
  44.  
  45. //初始化所有自动配置类,调用ApplicationContext的refresh()方法
  46. refreshContext(context);
  47.  
  48. //空方法
  49. afterRefresh(context, applicationArguments);
  50.  
  51. /关闭任务执行时间监听器
  52. stopWatch.stop();
  53.  
  54. //如果开启日志,则打印执行是时间
  55. if (this.logStartupInfo) {
  56. new StartupInfoLogger(this.mainApplicationClass)
  57. .logStarted(getApplicationLog(), stopWatch);
  58. }
  59.  
  60. //调用所有的SpringApplicationRunListener的started()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
  61. listeners.started(context);
  62.  
  63. //遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
  64. //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
  65. callRunners(context, applicationArguments);
  66. }
  67. catch (Throwable ex) {
  68. handleRunFailure(context, ex, exceptionReporters, listeners);
  69. throw new IllegalStateException(ex);
  70. }
  71.  
  72. try {
  73. //调用所有的SpringApplicationRunListener的running()方法,广播SpringBoot已经可以处理服务请求了。
  74. listeners.running(context);
  75. }
  76. catch (Throwable ex) {
  77. handleRunFailure(context, ex, exceptionReporters, null);
  78. throw new IllegalStateException(ex);
  79. }
  80. return context;
  81. }

由上文可知,默认WebApplicationType是WebApplicationType.SERVLET,所以默认的上下文是AnnotationConfigServletWebServerApplicationContext。

  1. //应用程序非web环境
  2. public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
  3. + "annotation.AnnotationConfigApplicationContext";
  4.  
  5. //应用程序web环境
  6. public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
  7. + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
  8.  
  9. //应用程序响应式web环境
  10. public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
  11. + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
  12.  
  13. public enum WebApplicationType {
  14.  
  15. //应用程序不需要任何应用服务器
  16. NONE,
  17.  
  18. //应用程序内嵌web服务器
  19. SERVLET,
  20.  
  21. //应用程序内嵌响应式web服务器
  22. REACTIVE
  23.  
  24. }
  25.  
  26. protected ConfigurableApplicationContext createApplicationContext() {
  27. Class<?> contextClass = this.applicationContextClass;
  28. if (contextClass == null) {
  29. try {
  30. switch (this.webApplicationType) {
  31. case SERVLET:
  32. contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
  33. break;
  34. case REACTIVE:
  35. contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
  36. break;
  37. default:
  38. contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
  39. }
  40. }
  41. catch (ClassNotFoundException ex) {
  42. throw new IllegalStateException(
  43. "Unable create a default ApplicationContext, "
  44. + "please specify an ApplicationContextClass",
  45. ex);
  46. }
  47. }
  48. return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  49. }

AnnotationConfigServletWebServerApplicationContext类结构层次如下。

父类ServletWebServerApplicationContext创建内嵌web应用服务器如下。

  1. @Override
  2. protected void onRefresh() {
  3. super.onRefresh();
  4. try {
  5. //创建web应用服务
  6. createWebServer();
  7. }
  8. catch (Throwable ex) {
  9. throw new ApplicationContextException("Unable to start web server", ex);
  10. }
  11. }
  12.  
  13. private void createWebServer() {
  14. WebServer webServer = this.webServer;
  15. ServletContext servletContext = getServletContext();
  16. if (webServer == null && servletContext == null) {
  17. //获取ServletWebServerFactory类型的web服务器工厂类,比如TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory
  18. ServletWebServerFactory factory = getWebServerFactory();
  19. this.webServer = factory.getWebServer(getSelfInitializer());
  20. }
  21. else if (servletContext != null) {
  22. try {
  23. getSelfInitializer().onStartup(servletContext);
  24. }
  25. catch (ServletException ex) {
  26. throw new ApplicationContextException("Cannot initialize servlet context",
  27. ex);
  28. }
  29. }
  30. initPropertySources();
  31. }

Springmvc项目上下文和Springboot项目上下文浅析

Springmvc项目上下文

Springmvc项目的rootcontext的创建时通过 xml中 配置的org.springframework.web.context.ContextLoaderListener,其父类ContextLoader中有一个初始化上下文的方法,如下。
  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext);

上下文创建好之后调用

  1. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

其中 servletContext的实例是 org.apache.catalina.core.ApplicationContext;

  1. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = org.springframework.web.context.WebApplicationContext.ROOT

这样rootcontext就创建好了,并且放入了servletContext中保存。rootcontext获取方式如下。

  1. WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

Springmvc项目中的DispatcherServlet是在xml中按照servlet格式配置的,这种方式创建的servlet实例没有被spring容器管理。

DispatcherServlet实现了ApplicationContextAware接口,有一个成员变量来保存此servlet对应的上下文,如下。
  1. /** WebApplicationContext for this servlet */
  2. private WebApplicationContext webApplicationContext;

这种情况下webApplicationContext变量是无法注入的【DispatcherServlet实例没有被spring容器管理】。看一下DispatcherServlet的父类FrameworkServlet是如何初始化上下文的,如下。

  1. protected WebApplicationContext initWebApplicationContext() {
  2. WebApplicationContext rootContext =
  3. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  4. WebApplicationContext wac = null;
  5. //springmvc项目这块的判断为false;springboot项目为ture。
  6. if (this.webApplicationContext != null) {
  7.  
  8. ......

DispatcherServlet所属上下文的存储

  1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. ...
  3. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  4. ...
  5. }

DispatcherServlet所属上下文获取org.springframework.web.servlet.support.RequestContextUtils。

Springboot项目上下文

Springboot项目中,调试代码时发现DispatcherServlet的父类FrameworkServlet在初始化上下文的时候rootcontext 和 DispatcherServlet成员变量webApplicationContext保存的是一个实例,即AnnotationConfigServletWebServerApplicationContext实例。
上面也提到了DispatcherServlet【对应的实例被spring容器管理】实现了ApplicationContextAware接口,webApplicationContext保存的上下文是通过自动注入而来。
RootContext(AnnotationConfigServletWebServerApplicationContext)保存到servletcontext中的操作,如下。
  1. //ServletWebServerApplicationContext
    protected void prepareWebApplicationContext(ServletContext servletContext) {
  2. ...
  3. servletContext.setAttribute(
  4. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
  5. ...
  6. }

总结

经过三篇文章的分析,相信大家已经明白了Springmvc项目默认是有两个上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,对应的类型是XmlServletWebServerApplicationContext),而Springboot项目默认是一个上下文,对应的类型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑问,请关注订阅号,进行私聊。

 

Springboot源码——应用程序上下文分析的更多相关文章

  1. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  2. SpringBoot源码篇:深度分析SpringBoot如何省去web.xml

    一.前言 从本博文开始,正式开启Spring及SpringBoot源码分析之旅.这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识.从本文开始我会持续更新, ...

  3. SpringBoot源码解析系列文章汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...

  4. SpringBoot是如何实现自动配置的?--SpringBoot源码(四)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三 ...

  5. 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何搭建自己的SpringBoot源码调试环境?--SpringBoot源码(一). 前面搭建好了自己本地的S ...

  6. SpringBoot的启动流程是怎样的?SpringBoot源码(七)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 温故而知新 本篇接 SpringBoot内置的各种Starter是怎样构建的? SpringBoot源码(六) 温故而知新, ...

  7. springboot源码解读

    springboot源码从main函数开始 public static void main(String[] args) { ApplicationContext app = SpringApplic ...

  8. Springboot源码分析之项目结构

    Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...

  9. SpringBoot源码学习系列之异常处理自动配置

    SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...

随机推荐

  1. 三层登录—c#

    学习了三层,有一个登录窗口的小练习.是我们第一次接触三层的初战.如今仅仅是简单的了解了一些,须要学习的还有非常多,以下浅谈自己的理解. 我们说的三层就是分层了显示层.业务逻辑层和数据訪问层.当中显示层 ...

  2. 50个Android开发技巧(09 避免用EditText对日期进行验证)

    我们都知道,在表单中对数据进行验证不但无聊并且easy出错. (原文地址:http://blog.csdn.net/vector_yi/article/details/24424713) 想象一下,一 ...

  3. nyoj--42--一笔画问题(并查集)

    一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来. ...

  4. vs2015发布项目到虚拟主机组策略阻止csc.exe程序问题

    这个问题之前碰到过一次,这次又碰到,就记录一下解决方法. 这个问题的产生的原因,据说是虚拟主机没有权限执行exe文件造成的,如果是独立服务器的话发布就不会出现这个问题. 使用VS2015发布web项目 ...

  5. spring boot 的常用注解使用 总结

    附:Spring Boot 官方文档学习(一)入门及使用见https://www.cnblogs.com/larryzeal/p/5799195.html @RestController和@Reque ...

  6. RMAN备份脚本--DataGuard primary

    单机环境全备   export ORACLE_BASE=/oracle export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1 export ORACL ...

  7. Pepper plugin implementation

    For Developers‎ > ‎Design Documents‎ > ‎ Pepper plugin implementation This document provides a ...

  8. 移动端viewport解惑

    我们在做移动端webapp的时候需要设置这么一段: <meta name="viewport" content="width=device-width, initi ...

  9. CSS 预处理语言之 less 篇

    less 前言 Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量.混合(mixin).函数等功能,让 CSS 更易维护.方便制作主题.扩充. 安装 客户端使用 // 引入 ...

  10. mysql每个表总的索引大小

    /* 指定的数据库 每个表的索引 不包含主键索引的大小*/ ,),,),'mb') as index_size from information_schema.tables where TABLE_S ...