文章目录
- 一、创建SpringApplication对象
- (1)外部run()
- (2)new SpringApplication()
- (3)initialize()
- (3.1)sources
- (3.2)deduceWebEnvironment()
- (3.3)setInitializers()
- getSpringFactoriesInstances()
- createSpringFactoriesInstances()
- (3.4)setListeners()
- (3.5)deduceMainApplicationClass
- (3.6)总结
- 二、调用SpringApplication.run()
- (1)SpringApplicationRunListeners
SpringBoot的启动流程虽然复杂,但是认真分析还是可以做出重要且有代表性的总结的!接下来,我们从一个简单的SpringBoot应用对SpringBoot的启动流程进行分析!
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldMainApplication.class, args);
}
}
一、创建SpringApplication对象
(1)外部run()
首先,调用run()方法
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
随后进入重构run()方法:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
创建一个SpringApplication对象出来,然后调用对象的run()方法,我们先看看这个创建SpringApplication的过程:
(2)new SpringApplication()
SpringApplication的构造函数,其中调用了initialize()方法!
public SpringApplication(Object... sources) {
initialize(sources);
}
看下initialize()方法的具体逻辑:
(3)initialize()
private void initialize(Object[] sources) {
//1
if(sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//2
this.webEnvironment = deduceWebEnvironment();
//3
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//4
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//5
this.mainApplicationClass = deduceMainApplicationClass();
}
主要逻辑:初始化SpringApplication对象的成员变量(sources,webEnvironment,initializers,listeners,mainApplicationClass),接下来,分析每一个初始化动作!
(3.1)sources
保存主配置类…
if(sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
(3.2)deduceWebEnvironment()
this.webEnvironment = deduceWebEnvironment();
private boolean deduceWebEnvironment() {
for(String className : WEB_ENVIRONMENT_CLASSES) {
if(!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
其中WEB_ENVIRONMENT_CLASSES
是一个静态常量数组:
privatestaticfinal String[] WEB_ENVIRONMENT_CLASSES = {
"javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
逻辑很简单:根据org.springframework.util.ClassUtils的静态方法去判断classpath下是否有
WEB_ENVIRONMENT_CLASSES
包含的类
- 有包含则返回true则表示启动一个WEB应用
- 否则返回false启动一个标准Spring的应用。
(3.3)setInitializers()
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
initializers成员变量是一个ApplicationContextInitializer类型对象的集合。顾名思义,ApplicationContextInitializer是一个可以用来初始化ApplicationContext的接口。
private List<ApplicationContextInitializer<?>> initializers;
private void initialize(Object[] sources) {
...
// 为成员变量initializers赋值
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
...
}
public void setInitializers(Collection<? extends
ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
初始化classpath下的所有的可用的应用上下文初始化器ApplicationContextInitializer
关键是调用getSpringFactoriesInstances(ApplicationContextInitializer.class)
,来获取ApplicationContextInitializer类型对象的列表。
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 =
createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
主要看 getSpringFactoriesInstances()、createSpringFactoriesInstances()
在 getSpringFactoriesInstances() 方法中,
1. 首先通过调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字存入一个Set中.
2. 然后调用createSpringFactoriesInstances()方法根据读取到的名字创建对象。
3. 最后会将创建好的对象列表排序并返回。 ----> 我们看一下黄色标记的方法
getSpringFactoriesInstances()
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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);
}
}
从一个名字叫spring.factories的资源文件中,读取key为org.springframework.context.ApplicationContextInitializer的value。spring.factories的部分内容如下:
"摘自spring-boot-1.3.3.RELEASE.jar中的资源文件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"
仍然是在getSpringFactoriesInstances()方法中:接下来会调用
createSpringFactoriesInstances()
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size()); //1
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);
}
}
return instances;
}
在1处可以看到:
SpringApplication对象的成员变量initalizers就被初始化为,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer这四个类的对象组成的list(ArrayList)。
(3.4)setListeners()
结论:初始化classpath下的所有的可用的ApplicationListener
setListeners((Collection)
getSpringFactoriesInstances(ApplicationListener.class));
"摘自:org.springframework.boot.SpringApplication"
private List<ApplicationListener<?>> listeners;
private void initialize(Object[] sources) {
// 为成员变量listeners赋值
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
...
}
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>();
this.listeners.addAll(listeners);
}
listeners成员变量,是一个ApplicationListener<?>类型对象的集合。可以看到获取该成员变量内容使用的是跟成员变量initializers一样的方法,只不过传入的类型从ApplicationContextInitializer.class变成了ApplicationListener.class。
"摘自spring-boot-1.3.3.RELEASE.jar中的资源文件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最终会被初始化为以上这几个类的对象组成的list。
(3.5)deduceMainApplicationClass
"代码摘自:org.springframework.boot.SpringApplication"
private Class<?> mainApplicationClass;
private void initialize(Object[] sources) {
...
// 为成员变量mainApplicationClass赋值
this.mainApplicationClass = deduceMainApplicationClass();
...
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for(StackTraceElement stackTraceElement : stackTrace) {
if("main".equals(stackTraceElement.getMethodName())) { //1
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
在该方法中,通过获取当前调用栈,找到入口方法main所在的类,并将其赋值给SpringApplication对象的成员变量mainApplicationClass。在我们的例子中mainApplicationClass即是我们自己编写的Application类。
1处:从多个配置类中寻找有main()方法的主配置类
至此,SpringApplication对象就创建出来了!
(3.6)总结
1) 保存主配置类(也就是示例中的HelloWorldMainApplication)
2) 判断当前是否一个web应用(是否导入web模块)
3) 从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
4) 从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
5) 从多个配置类中找到有main方法的主配置类
二、调用SpringApplication.run()
"摘自:org.springframework.boot.SpringApplication"
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
context = createAndRefreshContext(listeners, applicationArguments);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if(this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
}
StopWatch是来自org.springframework.util的工具类,可以用来方便的记录程序的运行时间。
关注点:为以下动作
(1)SpringApplicationRunListeners
"以下代码摘自:org.springframework.boot.SpringApplication"
public ConfigurableApplicationContext run(String... args) {
// 0
SpringApplicationRunListeners listeners = getRunListeners(args);
// 0.1
listeners.started();
try {...
// 1 + 1.1
ConfigurableEnvironment environment =
prepareEnvironment(listeners, applicationArguments);
// 2
context = createApplicationContext();
// 3
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
// 4
refreshContext(context);
// 5
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
listeners.finished(context, null);
}...
// 6
return context;
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
主要步骤:
0) 获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
0.1) 回调所有的获取SpringApplicationRunListener.starting()方法
1) 准备环境
1.1) 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
2) 创建ApplicationContext;决定创建web的ioc还是普通的ioc
3) 准备上下文环境;将environment保存到ioc中;• applyInitializers():迭代地回调之前保存的所有的ApplicationContextInitializer的initialize()方法
• 回调所有的SpringApplicationRunListener的contextPrepared();
4) 刷新容器;ioc容器初始化扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
• (如果是web应用还会创建嵌入式的Tomcat);
5) 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
6) 整个SpringBoot应用启动完成以后返回启动的ioc容器;