尚硅谷SpringBoot顶尖教程
SpringBoot应用从主启动类启动后的运行流程主要包含下面几个要点:
(1)准备环境
(2)刷新启动IOC容器
(3)回调容器中所有的ApplicationRunner,CommandLineRunner的run方法;
(4)监听器SpringApplicationRunListener回调finished方法
下面来看详细的启动流程分析, 课程讲解使用的是 1.5.10.RELEASE版本的SpringBoot源码。
主启动类的main方法是Springboot应用启动的入口。
@SpringBootApplication
public class SpringBoot02ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot02ConfigApplication.class, args);
}
}
通过SpringApplication.run方法会进行SpringApplication对象的创建及部分初始化工作。
public class SpringApplication {
// ...省略非核心代码
// 主启动类入口调用的run方法
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
// 继续走重载的run方法
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
// 第一步:调用SpringApplication构造方法实例化对象
// 第二步:创建SpringApplication对象后,继续调用重载的run方法
return new SpringApplication(sources).run(args);
}
// SpringApplication构造方法
public SpringApplication(Object... sources) {
// 初始化,参数是springboot应用的主启动类class对象
initialize(sources);
}
// 初始化
private void initialize(Object[] sources) {
// 保存主配置类到this.sources属性中
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判断当前springBoot应用是否是一个web应用,并保存判断结果到this.webEnvironment属性中
this.webEnvironment = deduceWebEnvironment();
// 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationContextInitializer并保存到this.initializers属性中。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationListener并保存到this.listeners属性中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplication
this.mainApplicationClass = deduceMainApplicationClass();
}
// 判断当前springBoot应用是否是一个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) {
// 判断当前环境是否存在ConfigurableWebApplicationContext
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
// 保存ApplicationContextInitializer到this.initializers属性中
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
// 保存ApplicationListener到this.listeners属性中
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>();
this.listeners.addAll(listeners);
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 从META-INF/spring.factories文件中查找ApplicationContextInitializer.class,ApplicationListener.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>(
// META-INF/spring.factories
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化ApplicationContextInitializer,ApplicationListener
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
}
// 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplication
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 通过当前的栈中找到有main方法的配置类,就是主启动类
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
spring-boot-1.5.10.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.embedded.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
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
当然类路径下还有其他spring.factories文件都有可能配置的有要初始化的组件类名称,自定义spring.factories也可以。比如: 1.5.10.RELEASE\spring-boot-autoconfigure-1.5.10.RELEASE.jar!\META-INF\spring.factories
中的初始化配置。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
运行run方法继续完善IOC容器内容,源码分析如下:
public class SpringApplication {
// ...
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 配置IOC容器开始
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
// 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用所有的SpringApplicationRunListener#starting方法
listeners.starting();
try {
// 封装命令行参数,主启动类main方法的args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 打印banner图标
Banner printedBanner = printBanner(environment);
// 创建IOC容器, 里面决定创建Web的IOC容器还是普通IOC容器。
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
// 准备上下文环境,将environment保存到ioc容器中;
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新IOC容器及初始化
refreshContext(context);
// IOC容器初始化完成后
afterRefresh(context, applicationArguments);
// 所有的SpringApplicationRunListener回调finished()方法
listeners.finished(context, null);
// 配置IOC容器结束
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成后返回启动的IOC容器
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
// 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// /META-INF/spring.factories
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
// type=SpringApplicationRunListener
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>(
// /META-INF/spring.factories
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建SpringApplicationRunListener实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
// 返回SpringApplicationRunListener实例
return instances;
}
// 调用所有的SpringApplicationRunListener#starting方法
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
// 准备环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 创建环境完成后调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
//调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
// 嵌入式WEB环境的IOC容器
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
// 普通IOC容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
// 创建IOC容器
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
// 创建web的IOC容器,还是普通的IOC容器
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
// 返回实例化的IOC容器
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
// 准备上下文环境,将environment保存到ioc容器中;
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将environment保存到ioc容器中
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 回调之前保存的所有ApplicationContextInitializer#initialize(context)方法
applyInitializers(context);
// 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 回调所有的SpringApplicationRunListener#contextLoaded(context)方法
listeners.contextLoaded(context);
}
// 回调之前保存的所有的ApplicationContextInitializer#initialize(context)方法
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// ApplicationContextInitializer#initialize(context)
initializer.initialize(context);
}
}
// 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
// SpringApplicationRunListener#contextPrepared(context),表示IOC容器准备完成
listener.contextPrepared(context);
}
}
// 回调所有的SpringApplicationRunListener#contextLoaded(context)方法
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
//SpringApplicationRunListener#contextLoaded(context), 表示IOC容器加载完成
listener.contextLoaded(context);
}
}
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
//刷新IOC容器
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。
((AbstractApplicationContext) applicationContext).refresh();
}
// IOC容器初始化完成后
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
//从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
//从IOC容器中获取所有的ApplicationRunner进行回调。
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
//从IOC容器中获取所有的CommandLineRunner进行回调。
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
//回调ApplicationRunner#run
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
//回调CommandLineRunner#run
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
// 所有的SpringApplicationRunListener回调finished()方法
public void finished(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFinishedListener(listener, context, exception);
}
}
private void callFinishedListener(SpringApplicationRunListener listener,
ConfigurableApplicationContext context, Throwable exception) {
// 回调SpringApplicationRunListener#finished
listener.finished(context, exception);
}
// ...
}
我们来看运行run方法的debug过程:
从类路径下/META-INF/spring.factories,获取SpringApplicationRunListener
spring-boot-1.5.10.RELEASE.jar!\META-INF\spring.factories
文件中的配置
debug调试结果:
回调所有的获取SpringApplicationRunListeners.starting方法
封装命令行参数:
准备创建环境,创建环境完成后回调SpringApplicationRunListener#environmentPrepared(environment)方法表示环境准备完成。
创建ApplicationContext容器,
里面决定创建Web的IOC容器还是普通IOC容器。
准备上下文环境,将environment保存到ioc容器中
applyInitializers(context) 回调之前保存的所有的ApplicationContextInitializer的initialize(context)方法
回调所有之前保存的SpringApplicationRunListener的contextPrepared(context)方法
prepareContext(…)方法运行完成后回调所有的SpringApplicationRunListener的contextLoaded(context)方法
刷新容器,IOC容器初始化,扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。
IOC容器初始化完成后,web应用(嵌入式tomcat)也已经启动完成。
从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。ApplicationRunner先回调,CommandLineRunner后回调。
所有的SpringApplicationRunListener回调finished方法
整个SpringBoot应用启动完成后返回启动的IOC容器
主要看几个重要的事件回调机制 :
ApplicationContextInitializer, SpringApplicationRunListener配置在spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories文件中;如果自定义xxxListener, 需要将自定义xxxListener或xxxInitializer放在当前应用类路径下的/META-INF/spring.factories文件中。
自定义ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("HelloApplicationContextInitializer.initialize....." + configurableApplicationContext.getClass().getSimpleName());
}
}
自定义SpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
public HelloSpringApplicationRunListener(SpringApplication application,
String[] args) {
}
// 开始ioc配置
@Override
public void starting() {
System.out.println("HelloSpringApplicationRunListener.starting.....");
}
// 准备环境
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Map<String, Object> systemProperties = environment.getSystemProperties();
System.out.println("HelloSpringApplicationRunListener.environmentPrepared...," + systemProperties.get("os.name")
+ ',' + systemProperties.get("java.home"));
}
// ioc容器准备
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.contextPrepared.....");
}
// ioc容器加载
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.contextLoaded.....");
}
// ioc容器配置完成
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("HelloSpringApplicationRunListener.finished....");
}
}
将自定义的ApplicationContextInitializer, SpringApplicationRunListener配置在当前项目类路径下的/META-INF/spring.factories
文件中
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
com.atgugui.listener.HelloApplicationContextInitializer
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.atgugui.listener.HelloSpringApplicationRunListener
ApplicationRunner,CommandLineRunner组件只要放在IOC容器中就可以了;自定义xxxRunner,类上面加@Component即可.
/**
* ApplicationRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行
*/
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("HelloApplicationRunner.run.....");
}
}
/**
* CommandLineRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行
*/
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("HelloCommandLineRunner.run..." + Arrays.asList(args));
}
}
配置完成,启动应用,查看启动日志,和上面分析的源码执行顺序一样。
HelloSpringApplicationRunListener.starting 开始初始化
HelloSpringApplicationRunListener.environmentPrepared 准备环境
HelloApplicationContextInitializer.initialize方法初始化;
HelloSpringApplicationRunListener.contextPrepared方法准备上下文环境;
HelloSpringApplicationRunListener.contextLoaded方法加载上下文环境;