从SpringBoot主程序类入手,我们以debug的方式进行源码的解析。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
首先进入SpringApplication.class中,可以看到 run() 方法(注意与后面的run()方法区分)的作用是创建SpringApplication对象,之后调用对象的run()方法运行程序。
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
之后来看看对象是如何被创建出来的,这里主要关注 initialize() 这个初始化方法。
public SpringApplication(Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.initialize(sources);
}
在 initialize() 方法中主要分以下几步
private void initialize(Object[] sources) {
//1.保存多个配置类
if (sources != null && sources.length > 0){
this.sources.addAll(Arrays.asList(sources));
}
//2.判断是否为Web环境应用
this.webEnvironment = this.deduceWebEnvironment();
//3.扫描并保存所有ApplicationContextInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//4.扫描并保存所有ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//5.查找主配置类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
以下部分是对上面步骤的详解:
private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private boolean deduceWebEnvironment() {
String[] var1 = WEB_ENVIRONMENT_CLASSES;
...
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
...
#Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
...
private Class<?> deduceMainApplicationClass() {
try {
...
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}
SpringApplication对象创建完成后,我们来看看它是怎么被运行起来的。
public ConfigurableApplicationContext run(String... args) {
...
//1. 扫描并保存所有SpringApplicationRunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//回调所有的SpringApplicationRunListeners的starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//打印Spring图标
Banner printedBanner = this.printBanner(environment);
//3.创建ApplicationContext(IOC容器)
context = this.createApplicationContext();
new FailureAnalyzers(context);
//4.准备上下文环境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5.刷新容器(扫描,创建,加载所有组件的地方:配置类,组件,自动配置)
this.refreshContext(context);
//6.完成容器刷新
this.afterRefresh(context, applicationArguments);
//所有的pringApplicationRunListeners回调finished()方法
listeners.finished(context, (Throwable)null);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成,并返回启动的IOC容器
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
以下部分是对上面步骤的详解:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
...
listeners.environmentPrepared((ConfigurableEnvironment)environment);
protected ConfigurableApplicationContext createApplicationContext() {
...
contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");
...
return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
...
this.applyInitializers(context);
listeners.contextPrepared(context);
...
public void refresh() throws BeansException, IllegalStateException {
...
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
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());
...
配置在META-INF/spring.factories中
只需要放在IOC容器中
监听过程解析
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize"+applicationContext);
}
}
可以看到它是一个泛型接口,SpringBoot已经提供了许多初始化器(如下图),这里我们监听IOC容器的初始化器,观察ApplicationContextInitializer运行过程。
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...starting");
}
@Override
public void environmentPrepared(ConfigurableEnvironment configurableEnvironment) {
Object o = configurableEnvironment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener...environmentPrepared"+o); //测试准备环境,获取系统版本
}
@Override
public void contextPrepared(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("SpringApplicationRunListener...contextPrepared");
}
@Override
public void contextLoaded(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("SpringApplicationRunListener...contextLoaded");
}
@Override
public void finished(ConfigurableApplicationContext configurableApplicationContext, Throwable throwable) {
System.out.println("SpringApplicationRunListener...finished");
}
}
观察b.运行run方法时,SpringApplicationRunListener的各个方法的执行过程。
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
System.out.println(" ApplicationRunner...run");
}
}
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
System.out.println("CommandLineRunner...run..."+ Arrays.asList(strings));
}
}
org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener
SpringApplicationRunListener...starting <--
SpringApplicationRunListener...environmentPreparedWindows 10 <--
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.20.RELEASE)
ApplicationContextInitializer...initializeorg.springframework.context.annotation.AnnotationConfigApplicationContext@6c9f5c0d: startup date [Thu Jan 01 08:00:00 CST 1970]; root of context hierarchy
SpringApplicationRunListener...contextPrepared <--
2019-05-02 23:50:36.471 INFO 2464 --- [ main] c.a.springboot.Springboot07Application : Starting Springboot07Application on DESKTOP-IPI86R4 with PID 2464 (C:\Other\CodeWorkplace\java\SpringBoot-Examples\springboot-07\target\classes started by Kellen-PC in C:\Other\CodeWorkplace\java\SpringBoot-Examples\springboot-07)
2019-05-02 23:50:36.473 INFO 2464 --- [ main] c.a.springboot.Springboot07Application : No active profile set, falling back to default profiles: default
SpringApplicationRunListener...contextLoaded <--
2019-05-02 23:50:36.517 INFO 2464 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6c9f5c0d: startup date [Thu May 02 23:50:36 CST 2019]; root of context hierarchy
2019-05-02 23:50:36.961 INFO 2464 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
ApplicationRunner...run <--
CommandLineRunner...run...[] <--
SpringApplicationRunListener...finished <--
2019-05-02 23:50:36.969 INFO 2464 --- [ main] c.a.springboot.Springboot07Application : Started Springboot07Application in 0.706 seconds (JVM running for 1.663)
2019-05-02 23:50:36.970 INFO 2464 --- [ Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6c9f5c0d: startup date [Thu May 02 23:50:36 CST 2019]; root of context hierarchy
2019-05-02 23:50:36.971 INFO 2464 --- [ Thread-3] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown