Springboot源码——应用程序上下文分析
前两篇(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,UndertowServletWebServerFactory
- ServletWebServerFactory 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项目上下文
- 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容器管理。
- /** 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项目上下文
- //ServletWebServerApplicationContext
protected void prepareWebApplicationContext(ServletContext servletContext) {- ...
- servletContext.setAttribute(
- WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
- ...
- }
总结
经过三篇文章的分析,相信大家已经明白了Springmvc项目默认是有两个上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,对应的类型是XmlServletWebServerApplicationContext),而Springboot项目默认是一个上下文,对应的类型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑问,请关注订阅号,进行私聊。
Springboot源码——应用程序上下文分析的更多相关文章
- SpringBoot源码分析之SpringBoot的启动过程
SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30 | 分类于 springboot | 0 Comments | 阅读次数 SpringB ...
- SpringBoot源码篇:深度分析SpringBoot如何省去web.xml
一.前言 从本博文开始,正式开启Spring及SpringBoot源码分析之旅.这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识.从本文开始我会持续更新, ...
- SpringBoot源码解析系列文章汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...
- SpringBoot是如何实现自动配置的?--SpringBoot源码(四)
注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三 ...
- 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)
注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何搭建自己的SpringBoot源码调试环境?--SpringBoot源码(一). 前面搭建好了自己本地的S ...
- SpringBoot的启动流程是怎样的?SpringBoot源码(七)
注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 温故而知新 本篇接 SpringBoot内置的各种Starter是怎样构建的? SpringBoot源码(六) 温故而知新, ...
- springboot源码解读
springboot源码从main函数开始 public static void main(String[] args) { ApplicationContext app = SpringApplic ...
- Springboot源码分析之项目结构
Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...
- SpringBoot源码学习系列之异常处理自动配置
SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...
随机推荐
- 三层登录—c#
学习了三层,有一个登录窗口的小练习.是我们第一次接触三层的初战.如今仅仅是简单的了解了一些,须要学习的还有非常多,以下浅谈自己的理解. 我们说的三层就是分层了显示层.业务逻辑层和数据訪问层.当中显示层 ...
- 50个Android开发技巧(09 避免用EditText对日期进行验证)
我们都知道,在表单中对数据进行验证不但无聊并且easy出错. (原文地址:http://blog.csdn.net/vector_yi/article/details/24424713) 想象一下,一 ...
- nyoj--42--一笔画问题(并查集)
一笔画问题 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来. ...
- vs2015发布项目到虚拟主机组策略阻止csc.exe程序问题
这个问题之前碰到过一次,这次又碰到,就记录一下解决方法. 这个问题的产生的原因,据说是虚拟主机没有权限执行exe文件造成的,如果是独立服务器的话发布就不会出现这个问题. 使用VS2015发布web项目 ...
- spring boot 的常用注解使用 总结
附:Spring Boot 官方文档学习(一)入门及使用见https://www.cnblogs.com/larryzeal/p/5799195.html @RestController和@Reque ...
- RMAN备份脚本--DataGuard primary
单机环境全备 export ORACLE_BASE=/oracle export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1 export ORACL ...
- Pepper plugin implementation
For Developers > Design Documents > Pepper plugin implementation This document provides a ...
- 移动端viewport解惑
我们在做移动端webapp的时候需要设置这么一段: <meta name="viewport" content="width=device-width, initi ...
- CSS 预处理语言之 less 篇
less 前言 Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量.混合(mixin).函数等功能,让 CSS 更易维护.方便制作主题.扩充. 安装 客户端使用 // 引入 ...
- mysql每个表总的索引大小
/* 指定的数据库 每个表的索引 不包含主键索引的大小*/ ,),,),'mb') as index_size from information_schema.tables where TABLE_S ...