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

SpringApplication引导类

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

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

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

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."+ "web.reactive.DispatcherHandler";private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."+ "web.servlet.DispatcherServlet";private WebApplicationType deduceWebApplicationType() {if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : WEB_ENVIRONMENT_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;
}

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

public ConfigurableApplicationContext run(String... args) {//开启任务执行时间监听器StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();//设置系统属性『java.awt.headless』,为true则启用headless模式支持
    configureHeadlessProperty();//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,//之后逐个调用其started()方法,广播SpringBoot要开始执行了。SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。                ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);//是否搜索BeanInfo类
        configureIgnoreBeanInfo(environment);//Banner打印Banner printedBanner = printBanner(environment);//根据WebApplicationType的值来决定创建何种类型的ApplicationContext对象context = createApplicationContext();//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
        prepareContext(context, environment, listeners, applicationArguments,printedBanner);//初始化所有自动配置类,调用ApplicationContext的refresh()方法
        refreshContext(context);//空方法
        afterRefresh(context, applicationArguments);/关闭任务执行时间监听器stopWatch.stop();//如果开启日志,则打印执行是时间if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//调用所有的SpringApplicationRunListener的started()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
        listeners.started(context);//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
        callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {//调用所有的SpringApplicationRunListener的running()方法,广播SpringBoot已经可以处理服务请求了。
        listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;
}

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

//应用程序非web环境
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";//应用程序web环境
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";//应用程序响应式web环境
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";public enum WebApplicationType {//应用程序不需要任何应用服务器
    NONE,//应用程序内嵌web服务器
    SERVLET,//应用程序内嵌响应式web服务器
    REACTIVE}protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

AnnotationConfigServletWebServerApplicationContext类结构层次如下。

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

@Override
protected void onRefresh() {super.onRefresh();try {//创建web应用服务
        createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//获取ServletWebServerFactory类型的web服务器工厂类,比如TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactoryServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();
}

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

Springmvc项目上下文

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

上下文创建好之后调用

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

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

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

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

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

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

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

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

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

DispatcherServlet所属上下文的存储

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {...request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());...
}

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

Springboot项目上下文

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

总结

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

转载于:https://www.cnblogs.com/hujunzheng/p/10854464.html

Springboot源码——应用程序上下文分析相关推荐

  1. 计算机毕业设计springboot基于疫情背景下的新型点餐送餐系统bpe1s源码+系统+程序+lw文档+部署

    计算机毕业设计springboot基于疫情背景下的新型点餐送餐系统bpe1s源码+系统+程序+lw文档+部署 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 开发软件:idea ecli ...

  2. 计算机毕业设计springboot交通事故档案管理平台ryug8源码+系统+程序+lw文档+部署

    计算机毕业设计springboot交通事故档案管理平台ryug8源码+系统+程序+lw文档+部署 计算机毕业设计springboot交通事故档案管理平台ryug8源码+系统+程序+lw文档+部署 本源 ...

  3. 计算机毕业设计springboot基于Springboot的在线教育平台的设计与实现8qecq源码+系统+程序+lw文档+部署

    计算机毕业设计springboot基于Springboot的在线教育平台的设计与实现8qecq源码+系统+程序+lw文档+部署 计算机毕业设计springboot基于Springboot的在线教育平台 ...

  4. 计算机毕业设计springboot教学事务流转与管理平台k0446源码+系统+程序+lw文档+部署

    计算机毕业设计springboot教学事务流转与管理平台k0446源码+系统+程序+lw文档+部署 计算机毕业设计springboot教学事务流转与管理平台k0446源码+系统+程序+lw文档+部署 ...

  5. 计算机毕业设计springboot计算机学院师生招聘系统c093h源码+系统+程序+lw文档+部署

    计算机毕业设计springboot计算机学院师生招聘系统c093h源码+系统+程序+lw文档+部署 计算机毕业设计springboot计算机学院师生招聘系统c093h源码+系统+程序+lw文档+部署 ...

  6. 计算机毕业设计springboot交通违章管理系统的设计与实现s7830源码+系统+程序+lw文档+部署

    计算机毕业设计springboot交通违章管理系统的设计与实现s7830源码+系统+程序+lw文档+部署 计算机毕业设计springboot交通违章管理系统的设计与实现s7830源码+系统+程序+lw ...

  7. 计算机毕业设计springboot酒店客房管理系统8yj0v源码+系统+程序+lw文档+部署

    计算机毕业设计springboot酒店客房管理系统8yj0v源码+系统+程序+lw文档+部署 计算机毕业设计springboot酒店客房管理系统8yj0v源码+系统+程序+lw文档+部署 本源码技术栈 ...

  8. 计算机毕业设计springboot晋中学院失物招领系统的设计与实现unst3源码+系统+程序+lw文档+部署

    计算机毕业设计springboot晋中学院失物招领系统的设计与实现unst3源码+系统+程序+lw文档+部署 计算机毕业设计springboot晋中学院失物招领系统的设计与实现unst3源码+系统+程 ...

  9. 计算机毕业设计springboot驾照一点通的设计与实现02bpd源码+系统+程序+lw文档+部署

    计算机毕业设计springboot驾照一点通的设计与实现02bpd源码+系统+程序+lw文档+部署 计算机毕业设计springboot驾照一点通的设计与实现02bpd源码+系统+程序+lw文档+部署 ...

最新文章

  1. smbpasswd 和 pdbedit 的区别
  2. 【读书笔记】iOS-ARC-不要向已经释放的对象发送消息
  3. 设计模式---(创建型)单例模式
  4. 尾递归对时间与空间复杂度的影响(上)
  5. linux删除软件包git的命令,linux系统安装git及git常用命令
  6. mysql 索引 lt =,当同时使用gt; =和lt; =时如何索引MySQL表?
  7. echarts饼状图mysql_echarts饼状图位置设置
  8. 声卡loopback有什么用_声卡购买须知,别买来又退整麻烦事的
  9. linux下配置环境变量方式
  10. java组合与继承始示例_Java 8特性与示例
  11. 重复组合公式及其证明方法
  12. 人工智能之自然语言处理初探
  13. winEdt下编辑报错:Something‘s wrong--perhaps a missing \item. \end{thebibliography}
  14. Python的raw string原始字串转化为string一般字符串,还有结尾插入‘\‘的方法,还有把string转化为raw string
  15. MATLAB 过时了吗?
  16. Android App收不到推送的消息
  17. JSD-2204-反射-Day17
  18. elasticsearch 怎么删除过期的数据
  19. realtek是什么意思_realtek bluetooth是什么意思
  20. 汉诺塔(Tower of hanoi)

热门文章

  1. 360浏览器收藏夹_换了一台电脑,浏览器收藏的网站不见了,咋办?
  2. java radio 不可选_在Java Swing中取消选择RadioButtons
  3. android保存字符到sd卡,android 保存TXT文件到SD卡方法
  4. SpringBoot2.x 整合 Ueditor
  5. 发送http和https请求工具类 Json封装数据
  6. Linux Shell脚本专栏_批量创建100用户并设置密码脚本_03
  7. 第3篇:Flowable-IDM详述
  8. 实战_05_SpringBoot整合redis单机版本
  9. MyBatis-Plus_入门试炼03
  10. Java-打印三角形