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 ...
随机推荐
- mysql-高级语言语法
一.注释 从#字符到行尾 从'-- '序列到行尾.两个破折号之后至少要有一个空格符或制表符. 二.设置变量 用户变量的形式为@var_name,其中变量名varname可以有当前字符集的文字数字字符. ...
- android 自己定义dialog并实现失去焦点(背景透明)的功能
前言:因为在项目中须要用到更新显示动画的需求,所以想到了dialog,自己定义dialog不难.网上教程非常多,可是在实现dialog背景透明的需求时,遇到了一点问题.网上的一些方法在我的机器上并没有 ...
- QtWebkit里RenderLayer树的绘制具体流程分析
更新:RenderLayer树的绘制对RenderObject的绘制.同一时候补足绘制阶段的描写叙述. QtWebkit里,QWebView,QWebPage和QWebFr ...
- WPF-MVVM-Demo
MVVM The model-view-viewmodel is a typically WPF pattern. It consists of a view that gets all the us ...
- CG 内置函数 和 HLSL 内置函数
CG 内置函数 英伟达官网链接: http://http.developer.nvidia.com/Cg/index_stdlib.html absacosallanyasinatan2atanbi ...
- List methods
Python provides methods that operate on lists. For example, append adds a new element to the end of ...
- Android5.0之后的页面切换动画
Android5.0之后给我们开发者剩了好多的事情,为什么这么说呢?还记得刚开始的时候,Android里面的所有的动画都要我们开发者自己来写,现在不需要了,因为5.0之后自带了好多的动画,比如:按钮点 ...
- 使用greenDAO遇到的问题
前一阵花时间学习了一下greenDAO的使用,这两天已经把项目中之前使用的sqlite数据库操作改用greenDAO,但是在改动的过程中还是出了一些问题,问题主要集中在主键上,下面整理了一下在改动过程 ...
- sapui5 One or more constraints have not been satisfied.
Getting error in creating a new project for UI5 One or more constraints have not been satisfied. slo ...
- Chrome Foundation Services
Chrome Foundation Services Contents Overview API Standards Service Directory Structure Dependencies ...