SpringBoot:Spring容器的启动过程
一、简述
Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用BeanFactory来实例化、配置和管理Bean。
二、SpringBoot的启动过程
在SpringBoot中,SpringApplication封装了一套Spring应用的启动流程,对用户完全是透明的,这个类原本在Spring中是没有的。
一般来说,默认的SpringApplication执行流程可以满足大部分需求,若是想要干预这过程,可以通过SpringApplication在流程的某些地方开启扩展点来对流程进行扩展,典型的扩展方案就是setXXX方法.
1 @SpringBootApplication
2 public class CodeSheepApplication {
3 public static void main( String[] args ) {
4 // SpringApplication.run( DdsApplication.class, args );
5 SpringApplication app = new SpringApplication( DdsApplication.class );
6 app.setXXX( ... ); // 用户自定的扩展在此 !!!
7 app.run( args );
8 }
9 }
SpringBoot应用中,首先要了解的就是SpringApplication这个类了。
SpringApplication的实例化,在上面这段代码中,使用了自定义SpringApplication,通过一行代码启动SpringBoot应用,也可以自定义SpringApplication的一些扩展。
1 @SuppressWarnings({ "unchecked", "rawtypes" })
2 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
3 this.resourceLoader = resourceLoader;
4 Assert.notNull(primarySources, "PrimarySources must not be null");
5 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
6 this.webApplicationType = WebApplicationType.deduceFromClasspath();
7 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
8 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
9 this.mainApplicationClass = deduceMainApplicationClass();
10 }
1.WebApplicationType是一个枚举Web Application的类型,其类型定义有三种:NONE(不应作为Web应用程序运行,也不应启动嵌入式Web服务器)、SERVLET(基于servlet的Web应用程序)、REACTIVE(响应式Web应用程序)。
NONE:org.springframework.context.annotation.AnnotationConfigApplicationContext
SERVLET:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
REACTIVE:org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
WebApplicationType#deduceFromClasspath()的意思是根据Classpath的内容推断WebApplication类型。
1 static WebApplicationType deduceFromClasspath() {
2 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
3 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
4 return WebApplicationType.REACTIVE;
5 }
6 for (String className : SERVLET_INDICATOR_CLASSES) {
7 if (!ClassUtils.isPresent(className, null)) {
8 return WebApplicationType.NONE;
9 }
10 }
11 return WebApplicationType.SERVLET;
12 }
ClassUtils是Spring框架所提供关于类级别的工具类,主要供框架内部使用。ClassUtils#isPresent是判断当前class loader中是否存在对应的类型。代码里面关于判断中的方法,为什么所提供的classLoader参数都为空,再进去方法里面看看,发现它是调用了ClassUtils#forName
1 public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
2 try {
3 forName(className, classLoader);
4 return true;
5 }
6 catch (IllegalAccessError err) {
7 throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
8 className + "]: " + err.getMessage(), err);
9 }
10 catch (Throwable ex) {
11 // Typically ClassNotFoundException or NoClassDefFoundError...
12 return false;
13 }
14 }
再看看ClassUtils#forName,方法的源码很长,它替换Class.forName(),还返回原始类型(例如“int”)和数组类名称(例如“String []”)的Class实例。 此外,它还能够以Java源代码样式解析内部类名(例如“java.lang.Thread.State”而不是“java.lang.Thread $ State”)。
里面调用了ClassUtils#resolvePrimitiveClassName来把给定的类作为基本类(如果适用的话);如果不是基本类型则在缓存找,如果是基本类型则返回;然后接着判断给出的类名是不是一维或多维的整型或字符串数组;接着判断方法传入的classLoader,为空则ClassUtils#getDefaultClassLoader来从当前System中获取默认的classLoader,再使用给定的类加载器返回与具有给定字符串名称的类或接口关联的Class对象→Class.forName(类名, false, 当前默认的classLoader),当找不到Class就会报异常。
1 public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
2 throws ClassNotFoundException, LinkageError {
3
4 Assert.notNull(name, "Name must not be null");
5
6 Class<?> clazz = resolvePrimitiveClassName(name);
7 if (clazz == null) {
8 clazz = commonClassCache.get(name);
9 }
10 if (clazz != null) {
11 return clazz;
12 }
13
14 // "java.lang.String[]" style arrays
15 if (name.endsWith(ARRAY_SUFFIX)) {
16 String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
17 Class<?> elementClass = forName(elementClassName, classLoader);
18 return Array.newInstance(elementClass, 0).getClass();
19 }
20
21 // "[Ljava.lang.String;" style arrays
22 if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
23 String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
24 Class<?> elementClass = forName(elementName, classLoader);
25 return Array.newInstance(elementClass, 0).getClass();
26 }
27
28 // "[[I" or "[[Ljava.lang.String;" style arrays
29 if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
30 String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
31 Class<?> elementClass = forName(elementName, classLoader);
32 return Array.newInstance(elementClass, 0).getClass();
33 }
34
35 ClassLoader clToUse = classLoader;
36 if (clToUse == null) {
37 clToUse = getDefaultClassLoader();
38 }
39 try {
40 return Class.forName(name, false, clToUse);
41 }
42 catch (ClassNotFoundException ex) {
43 int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
44 if (lastDotIndex != -1) {
45 String innerClassName =
46 name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
47 try {
48 return Class.forName(innerClassName, false, clToUse);
49 }
50 catch (ClassNotFoundException ex2) {
51 // Swallow - let original exception get through
52 }
53 }
54 throw ex;
55 }
56 }
总结WebApplicationType#deduceFromClasspath(),它通过ClassPath来推断WebApplication的类型,从当前系统默认的ClassLoader获取WebApplication类型相对应类的映射,从而判断WebApplication的类型。
1 setInitializers((Collection)
2 getSpringFactoriesInstances(ApplicationContextInitializer.class));
2.setInitializers(...)
使用 SpringFactoriesLoader 查找并加载 classpath下 META-INF/spring.factories
文件中所有可用的 ApplicationContextInitializer ---- 用于在ConfigurableApplicationContext#refresh() 之前初始化Spring ConfigurableApplicationContext的回调接口。
1 # PropertySource Loaders
2 org.springframework.boot.env.PropertySourceLoader=\
3 org.springframework.boot.env.PropertiesPropertySourceLoader,\
4 org.springframework.boot.env.YamlPropertySourceLoader
5
6 # Run Listeners
7 org.springframework.boot.SpringApplicationRunListener=\
8 org.springframework.boot.context.event.EventPublishingRunListener
9
10 # Error Reporters
11 org.springframework.boot.SpringBootExceptionReporter=\
12 org.springframework.boot.diagnostics.FailureAnalyzers
13
14 # Application Context Initializers
15 org.springframework.context.ApplicationContextInitializer=\
16 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
17 org.springframework.boot.context.ContextIdApplicationContextInitializer,\
18 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
19 org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
20
21 # Application Listeners
22 org.springframework.context.ApplicationListener=\
23 org.springframework.boot.ClearCachesApplicationListener,\
24 org.springframework.boot.builder.ParentContextCloserApplicationListener,\
25 org.springframework.boot.context.FileEncodingApplicationListener,\
26 org.springframework.boot.context.config.AnsiOutputApplicationListener,\
27 org.springframework.boot.context.config.ConfigFileApplicationListener,\
28 org.springframework.boot.context.config.DelegatingApplicationListener,\
29 org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
30 org.springframework.boot.context.logging.LoggingApplicationListener,\
31 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
32
33 # Environment Post Processors
34 org.springframework.boot.env.EnvironmentPostProcessor=\
35 org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
36 org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
37 org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
38
39 # Failure Analyzers
40 org.springframework.boot.diagnostics.FailureAnalyzer=\
41 org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
42 org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
43 org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
44 org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
45 org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
46 org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
47 org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
48 org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
49 org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
50 org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
51 org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
52 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
53 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
54
55 # FailureAnalysisReporters
56 org.springframework.boot.diagnostics.FailureAnalysisReporter=\
57 org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
3.setListeners(...)
使用SpringFactoriesLoader查找并加载 classpath下 META-INF/spring.factories
文件中的所有可用的 ApplicationListener ---- 应用程序事件侦听器实现的接口, 基于Observer设计模式的标准java.util.EventListener 接口。
4.deduceMainApplicationClass()
推断并设置main方法的定义类,通过Throwable#getStackTrace()方法返回堆栈中的元素,找出方法名为main的堆栈元素,再根据类名返回对应 的反射类
1 private Class<?> deduceMainApplicationClass() {
2 try {
3 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
4 for (StackTraceElement stackTraceElement : stackTrace) {
5 if ("main".equals(stackTraceElement.getMethodName())) {
6 return Class.forName(stackTraceElement.getClassName());
7 }
8 }
9 }
10 catch (ClassNotFoundException ex) {
11 // Swallow and continue
12 }
13 return null;
14 }
再来看看SpringBoot应用运行方法 SpringApplication#run
1 public ConfigurableApplicationContext run(String... args) {
2 StopWatch stopWatch = new StopWatch();
3 stopWatch.start();
4 ConfigurableApplicationContext context = null;
5 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
6 configureHeadlessProperty();
7 SpringApplicationRunListeners listeners = getRunListeners(args);
8 listeners.starting();
9 try {
10 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
11 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
12 configureIgnoreBeanInfo(environment);
13 Banner printedBanner = printBanner(environment);
14 context = createApplicationContext();
15 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
16 new Class[] { ConfigurableApplicationContext.class }, context);
17 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
18 refreshContext(context);
19 afterRefresh(context, applicationArguments);
20 stopWatch.stop();
21 if (this.logStartupInfo) {
22 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
23 }
24 listeners.started(context);
25 callRunners(context, applicationArguments);
26 }
27 catch (Throwable ex) {
28 handleRunFailure(context, ex, exceptionReporters, listeners);
29 throw new IllegalStateException(ex);
30 }
31
32 try {
33 listeners.running(context);
34 }
35 catch (Throwable ex) {
36 handleRunFailure(context, ex, exceptionReporters, null);
37 throw new IllegalStateException(ex);
38 }
39 return context;
40 }
三、总结
获取SpringApplicationListener → 通知Listeners start → 创建参数,配置Environment → 根据WebApplicationType创建ApplicationContext → 初始化ApplicationContext,设置Environment加载相关配置等 → 通知EnvironmentPrepared,contextLoaded → refresh ApplicationContext → 通知Listeners start context → 完成启动→ 通知runner → 结束
通过
SpringFactoriesLoader
加载META-INF/spring.factories
文件,获取并创建SpringApplicationRunListener
对象然后由
SpringApplicationRunListener
来发出 starting 消息创建参数,并配置当前 SpringBoot 应用将要使用的
Environment
完成之后,依然由
SpringApplicationRunListener
来发出environmentPrepared
消息根据
WebApplicationType
创建ApplicationContext
初始化
ApplicationContext
,并设置Environment
,加载相关配置等由
SpringApplicationRunListener
来发出contextPrepared
消息,告知SpringBoot 应用使用的ApplicationContext
已准备OK将各种 beans 装载入
ApplicationContext
,继续由SpringApplicationRunListener
来发出 contextLoaded 消息,告知 SpringBoot 应用使用的ApplicationContext
已装填OKrefresh ApplicationContext,完成IoC容器可用的最后一步
由
SpringApplicationRunListener
来发出 started 消息完成最终的程序的启动
由
SpringApplicationRunListener
来发出 running 消息,告知程序已运行起来了
SpringBoot:Spring容器的启动过程的更多相关文章
- spring容器的启动过程
spring的启动过程: 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环 ...
- Spring 容器的启动过程 流程图 自己看源码的梳理 如有错错误 请指正
- Spring源码系列——容器的启动过程(一)
一. 前言 Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气.俗话说,打蛇打七寸,那么Spring家族的"七寸"是什么呢?我心目中的答案一 ...
- Servlet容器的启动过程
[http://book.51cto.com/art/201408/448854.htm] Tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承Lifecycle接口,它管理着容器的整 ...
- Spring MVC的启动过程
一.概述 下面一个基本的运用springMVC的的web.xml的配置,这里要注意两个地方,一个是ContextLoadListener,一个是DispatcherServlet.web容器正是通过这 ...
- docker学习(3) 容器的启动过程
这一节我们来稍微了解下docker原理性的东西 docker run -i -t ubuntu /bin/bash 输入上面这行命令,启动一个ubuntu容器时,到底发生了什么? 大致过程可以用下图描 ...
- spring注解配置启动过程
最近看起spring源码,突然想知道没有web.xml的配置,spring是怎么通过一个继承于AbstractAnnotationConfigDispatcherServletInitializer的 ...
- 转:spring的启动过程-spring和springMVC父子容器的原理
要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...
- spring的启动过程就是创建ioc容器的过程
1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼 ...
随机推荐
- C# Redis学习系列二:Redis基本设置
上一篇:C# Redis学习系列一:Redis的认识.下载.安装.使用 一.redis 设置密码 使用下载好的 redis-cli.exe 指令: 1.设置密码: config set require ...
- 搭建http文件服务器 - Windows使用IIS搭建http文件服务器
- Spring Cloud Gateway 没有链路信息,我 TM 人傻了(上)
本系列是 我TM人傻了 系列第五期[捂脸],往期精彩回顾: 升级到Spring 5.3.x之后,GC次数急剧增加,我TM人傻了 这个大表走索引字段查询的 SQL 怎么就成全扫描了,我TM人傻了 获取异 ...
- 集群环境下的Session管理
1. 集群环境下的管理HTTPSSession所遇到的问题 一台服务器对应这个一个session对象,无法在另外一个服务器互通 解决方法: 1. Session 的 Replication(复制)将当 ...
- cas的基础配置
去除HTTPS的j基础认证方式 cas的:deployerConfigContext.xml <!-- Required for proxy ticket mechanism. -->&l ...
- Python - with 语句
管理外部资源的背景 在编程中会面临的一个常见问题是如何正确管理外部资源,例如文件.锁和网络连接 有时,程序会永远保留这些资源,即使不再需要它们,这种现象称为内存泄漏 因为每次创建和打开给定资源的新实例 ...
- 分析 ajax 请求并抓取 “今日头条的街拍图”
今日头条抓取页面: 分析街拍页面的 ajax 请求: 通过在 XHR 中查看内容,获取 url 链接,params 参数信息,将两者进行拼接后取得完整 url 地址.data 中的 article_u ...
- P3291-[SCOI2016]妖怪【凸壳】
正题 题目链接:https://www.luogu.com.cn/problem/P3291 题目大意 给出 \(n\) 个数字对 \((atk,dnf)\),求一个\((a,b)\). 对于每个数字 ...
- 我的Python学习记录
Python日期时间处理:time模块.datetime模块 Python提供了两个标准日期时间处理模块:--time.datetime模块. 那么,这两个模块的功能有什么相同和共同之处呢? 一般来说 ...
- Ubuntu系统的开机全流程介绍及grub美化
目录 前言 Ubuntu开机经历的步骤 BIOS Boot Loader Kernel 配置 Grub 的个性化主题 /usr/share/grub/default/grub /etc/default ...