这篇文章是以SpringIO的Cairo-SR7版本为例的。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
第一步,我们先来看看启动类:
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
从这里可以看出首先调用了SpringApplication的静态run方法:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
最终是创建了SpringApplication的实例并调用他的run方法,那么我们先来看看他是如何创建SpringApplication实例的吧。
第二步,创建SpringApplication实例对象:
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
代码不是很多,值得我们关注的代码只有四行,听我的准没错:
// 判断是否为Web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从MATA-INF/spring.factories中获取所有的ApplicationContextInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 同上,还需要解释吗?
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 获取启动类的全类名
this.mainApplicationClass = this.deduceMainApplicationClass();
至于为什么是从MATA-INF/spring.factories中获取所有的ApplicationContextInitializer,看完以下方法的调用过程你就明白了:
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
看完上面方法的调用过程,就很清晰了吧;至于这个ApplicationContextInitializer和ApplicationListener的作用是做什么的呢,我这篇文章不做解释,毕竟这不是这篇文章研究的方向。
第三步,SpringApplication实例对象创建好了,那么我们来看看他的run 方法吧:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
哈哈,是不是代码是不是比较多而且还无从下手,其实这里都是按照SpringApplicationRunListener接口方法的顺序回调的:
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}
首先调用getRunListeners方法获取SpringApplicationRunListener对象,立即调用他的starting方法;
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
这里你可能有个疑问,上面不是获取过listener了吗?为什么还要获取一遍?
因为构造器中获取的是ApplicationListener而这里获取的则是SpringApplicationListener,他们的作用是不一样的哟。
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
接下来该干嘛呢?按照SpringApplicationRunListener接口方法的顺序,我猜应该是要准备环境了吧;
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
那么准备的是什么环境?我们去看看吧。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
从上面代码可以看出创建的是Web环境,并且注意这一行代码:
listeners.environmentPrepared((ConfigurableEnvironment)environment);
和我猜的一样吧,是按SpringApplicationRunListener接口方法的顺序执行的,所以接下来我们看代码只要抓住SpringApplicationRunListener接口方法的顺序,就相当于抓住了SpringBoot启动流程了;来我们看看contextPrepared方法如何准备Spring的ApplicationContext对象吧;
context = this.createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
这里调用了BeanUtils来创建了ApplicationContext对象,但是没有设置任何东西呢;我们需要把Web环境设置到ApplicationContext对象中;
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
在准备ApplicationContext对象的时候我们需要注意这三行代码:
this.applyInitializers(context);
listeners.contextPrepared(context);
listeners.contextLoaded(context);
有没有发现SpringApplicationRunListener接口方法执行了一半了呢!是不是很开心;我们在构造器中获取很多初始化器,不知道你忘了没,他们就是在这里执行的哟,这是重点哦,千万别忘了,这篇文章中也不解释初始化器,这不是本篇文章的重点。
this.applyInitializers(context);
protected void applyInitializers(ConfigurableApplicationContext context) {
Iterator var2 = this.getInitializers().iterator();
while(var2.hasNext()) {
ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
现在ApplicationContext对象都准备好了,那么我来初始化所有的Java Bean对象吧;所有的@Bean、@Controller、@Configuration等对象都是在下面的方法中创建的;
this.refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
现在所有的对象都创建好了,那么就开始started我们的项目吧,再往下的方法基本上都是回调SpringApplicationRunListener接口方法剩下的方法了,没什么重要的逻辑了;
listeners.started(context);
public void started(ConfigurableApplicationContext context) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.started(context);
}
}
还有一个方法比较重要的,但是我也不知道他有什么用,总之就是重要;我只能看出ApplicationRunner比CommandLineRunner先执行,功力有限;
this.callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
最后就是我们的应用正在运行时调用running方法了,也没什么逻辑;
listeners.running(context);
public void running(ConfigurableApplicationContext context) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.running(context);
}
}
最后砸门就说拜拜啦。。。。
还有一个方法在run 方法出异常了才会调用哟;
this.handleRunFailure(context, var10, exceptionReporters, listeners);
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
this.handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
this.reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception var9) {
logger.warn("Unable to close ApplicationContext", var9);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
SpringBoot的运行流程到这里,就完全更你们掰扯完了,如果你能够从这篇文章中学到点东西那么就恭喜你了!!!
你可能会问知道流程有什么用?我会告诉你,基本上没有用,但是你可以自己实现SpringApplicationListener接口,然后放到MATA-INF/spring.factories中,在SpringBoot启动的时候做一点不可告人的事哦,坏笑。。。
自己思考一下怎么做,做出来后你就是掌握了SpringBoot运行流程了。
点赞,谢谢!