附3 springboot源码解析 - 构建SpringApplication
package com.microservice.framework; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class MySpringAplication { public void run(String[] args) {
SpringApplication sa = new SpringApplication(MySpringAplication.class);
sa.run(args);
} }
SpringBoot启动过程:
1、构建SpringApplication对象
2、执行run()
一、构建SpringApplication对象
/**
* The application context will load beans from the specified sources
*/
public SpringApplication(Object... sources) {
initialize(sources);
}
说明:
- 实例化该类的时候会加载bean到applicationContext中去
- 这里的入参是MySpringApplication.class这样一个Class<com.microservice.framework.MySpringApplication>对象
private final Set<Object> sources = new LinkedHashSet<Object>();
private boolean webEnvironment;
private Class<?> mainApplicationClass; private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
步骤:
- 将传入的MySpringApplication.class对象放入Set集合
- 判断是否是web环境
- 创建ApplicationInitializer列表
- 初始化ApplicationListener列表
- 初始化主类mainApplicationClass
1.1、将传入的MySpringApplication.class对象放入Set集合
1.2、判断是否是web环境:
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
说明:通过在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES这个数组中所包含的所有类(实际上就是2个类),如果存在那么当前程序即是一个Web应用程序,反之则不然。
1.3、创建ApplicationContextInitializer列表
private List<ApplicationContextInitializer<?>> initializers;
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = new ArrayList<T>(names.size());
// Create instances from the names
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
T instance = (T) constructor.newInstance(args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
步骤:
- 调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字,(这里是获取了四个ApplicationContextInitializer实现类的全类名,见下边)
- 为每一个Spring Factories根据读取到的名字创建其对象。(这里创建了4个对象)
- 将创建好的对象列表排序并返回。
其中,SpringFactoriesLoader.loadFactoryNames(type, classLoader)如下:
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; /**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
META-INF/spring-factories
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
说明:
- 从所有jar获取所有的META-INF/spring-factories文件。(这里只有spring-boot-1.3.0.RELEASE.jar下有一个)
- 遍历每一个spring-factories文件,并获取其下key为factoryClass.getName()(这里是入参
org.springframework.context.ApplicationContextInitializer)的value(这里有以上四个ApplicationContextInitializer实现类)
以上四个类的作用:

至此,设置ApplicationContextInitialize就完成了。
总结:整个setInitializers实际上就是初始化了SpringApplication的属性List<ApplicationContextInitializer<?>> initializers为一个ArrayList列表,该列表中有四个实例:
- ConfigurationWarningsApplicationContextInitializer的实例
- ContextIdApplicationContextInitializer的实例
- DelegatingApplicationContextInitializer实例
- ServerPortInfoApplicationContextInitializer实例
1.4、初始化ApplicationListener列表
private List<ApplicationListener<?>> listeners;
/**
* Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
* and registered with the {@link ApplicationContext}.
* @param listeners the listeners to set
*/
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>();
this.listeners.addAll(listeners);
}
META-INF/spring-factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
以上八个listener的作用如下:

至此,整个setListeners方法结束,初始化了一个包含以上8个ApplicationListener实例的List集合。
1.5、初始化主类mainApplicationClass
private Class<?> mainApplicationClass;
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
说明:获取main()方法所在的主类Class对象,并赋值给SpringApplication的mainApplicationClass属性。
至此,SpringApplication对象初始化完成了。
总结:整个SpringApplication初始化的过程,就是初始化了
- 一个包含入参MySpringApplication.class的sources的Set<Object>
- 一个当前环境是否是web环境的boolean webEnvironment
- 一个包含4个ApplicationContextInitializer实例的List
- 一个包含8个ApplicationListener实例的List
- 一个main方法所在的主类的Class对象。
注意:
本文基本参照http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow完成,该文的作者已经解析的很好了,我这里再抄一遍,只是为了加深记忆!!!
附3 springboot源码解析 - 构建SpringApplication的更多相关文章
- 【附3】springboot源码解析 - 构建SpringApplication
package com.microservice.framework; import org.springframework.boot.SpringApplication; import org.sp ...
- springboot源码解析 - 构建SpringApplication
1 package com.microservice.framework; 2 3 import org.springframework.boot.SpringApplication; 4 impor ...
- 附4 springboot源码解析-run()
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); / ...
- SpringBoot源码解析:创建SpringApplication对象实例
上篇文章SpringBoot自动装配原理解析中,我们分析了SpringBoot的自动装配原理以及@SpringBootApplication注解的原理,本篇文章则继续基于上篇文章中的main方法来分析 ...
- SpringBoot源码解析系列文章汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...
- springboot源码解析-管中窥豹系列之总体结构(一)
一.简介 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之项目类型(二)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Runner(三)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Initializer(四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
随机推荐
- 洛谷P2525 Uim的情人节礼物·其之壱 [康托展开]
题目传送门 Uim的情人节礼物·其之壱 题目描述 情人节到了,Uim打算给他的后宫们准备情人节礼物.UIm一共有N(1<=N<=9)个后宫妹子(现充去死 挫骨扬灰!). 为了维护他的后宫的 ...
- [ 原创 ]学习笔记-三种向ListView中填充简单文本的方法
Android 中ListView是很重要的一块内容 掌握ListView的基本用法 对学习安卓起着举足轻重的作用 今天就介绍一下三种向ListView 填充简单文本的方法 填充其他数据类型的用法之后 ...
- 机器学习之路:python 集成回归模型 随机森林回归RandomForestRegressor 极端随机森林回归ExtraTreesRegressor GradientBoostingRegressor回归 预测波士顿房价
python3 学习机器学习api 使用了三种集成回归模型 git: https://github.com/linyi0604/MachineLearning 代码: from sklearn.dat ...
- vijos 1880 变形最短路
题意:Ninian 的魔力可以在结界间传递.结界中有 N 个光柱,第 i 个光柱的光压范围为 0~Ei .魔力可以有 M 种传递,从光柱 Ai 传递到光柱 Bi ,花费时间 Ti .当魔力从光压为 S ...
- 解决CIFilter滤镜后图片大小和方向发生变化
调用contextWithOptions:和createCGImage: fromRect:方法创建CIContext.与以往不同的地方是CIImage没有frame与bounds属性:只有exten ...
- Ubntu 14.04 下 开源骨架跟踪-skeltrack
Skeltrack是个不错的开源骨架跟踪软件.跟踪起来还相对的稳定速度还不错.能满足基本的体感功能.下面来介绍下怎么安装. 1.运行环境配置 #need clutter 1.8 or greater ...
- git中如何合并某个指定文件?
分支A_bracn和B_branch,只想将A_branch分支的某个文件f.txt合并到B_branch分支上.git checkout A_branch git checkout --p ...
- AbstractAction
package cn.tz.action.abs; import java.io.File; import java.io.IOException; import java.text.SimpleDa ...
- oracle的dump理解
http://blog.csdn.net/notbaron/article/details/51228444 http://blog.csdn.net/lqx0405/article/details/ ...
- erlang资料
http://www.cnblogs.com/--00/tag/Erlang/ http://blog.csdn.net/turingbooks/article/details/3247749 htt ...