1. 环境搭建
代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见
tutorial-spring-boot-core/tutorial-spring-boot-listener
工程
1.1 配置文件
1. META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\
pers.masteryourself.tutorial.spring.boot.listener.extend.MyApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
pers.masteryourself.tutorial.spring.boot.listener.extend.MySpringApplicationRunListener
1.2 核心代码
1. MySpringApplicationRunListener
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
/**
* 必须要有这个构造函数
*
* @param application
* @param args
*/
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("SpringApplicationRunListener...environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...started...");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...running...");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...failed...");
}
}
2. MyApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("MyApplicationContextInitializer...initialize..." + applicationContext);
}
}
3. MyApplicationRunner
必须要放到容器中
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("MyApplicationRunner...run....");
}
}
4. MyCommandLineRunner
必须要放到容器中
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner...run..." + Arrays.asList(args));
}
}
2. 源码解析
2.1 流程图
2.2 核心代码剖析
1. org.springframework.boot.WebApplicationType#deduceFromClasspath
这个环境在之后的 getOrCreateEnvironment()
和 createApplicationContext()
方法均有用到
static WebApplicationType deduceFromClasspath() {
// 如果当前 ClassLoader 能加载到 "org.springframework.web.reactive.DispatcherHandler",那么就返回 REACTIVE 环境
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果加载不到 "javax.servlet.Servlet" 和 "org.springframework.web.context.ConfigurableWebApplicationContext"
// 那么就认为是 NONE,即非 web 环境
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 返回 SERVLET 环境
return WebApplicationType.SERVLET;
}
2. org.springframework.boot.SpringApplication#deduceMainApplicationClass
查找 main()
方法所在的类,这个方法很灵性,值得参考
private Class<?> deduceMainApplicationClass() {
try {
// 在当前代码中 new RuntimeException(),然后根据栈信息查找 main 方法所在的类
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;
}
3. org.springframework.boot.SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 之前已经判断过应用程序类型,根据类型选择是 Web IOC 容器还是普通的 IOC 容器
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4. org.springframework.boot.SpringApplication#callRunners
调用 ApplicationRunner
和 CommandLineRunner
组件的 run()
方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 注意这两个方法都是 Spring IOC 容器中获取的,所以使用它们只需要注入即可
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
// 调用 ApplicationRunner 的 run 方法
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
// 调用 CommandLineRunner 的 run 方法
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}