SpringApplication
的构造方法Springboot
的主启动类为:
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
其中 SpringApplication#run()
方法 调用的是如下静态方法:
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
最终使用 new 关键字构造了 SpringApplication
对象,然后调用了非静态 run()
方法。
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
...
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
...
}
return context;
}
构造 SpringApplication 对象时做了如下几件事:
- 获取 Bean Definition 源
- 推断应用类型
- 添加 ApplicationContext 初始化器
- 添加事件监听器
- 主类推断
package com.example.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import java.util.Collections;
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication spring = new SpringApplication(BootApplication.class);
// 设置 xml 的bean
spring.setSources(Collections.singleton("classpath:bean.xml"));
// 创建并初始化 Spring 容器
ConfigurableApplicationContext context = spring.run(args);
Arrays.stream(context.getBeanDefinitionNames()).forEach(i -> {
System.out.println("name: " + i +
" 来源: " + context.getBeanFactory().getBeanDefinition(i).getResourceDescription());
});
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd"
>
<bean id="bean1" class="com.example.boot.BootApplication.Bean1" />
beans>
输出
...
name: bootApplication 来源: null
name: bean1 来源: class path resource [bean.xml]
name: bean2 来源: com.example.boot.BootApplication
...
其中来源为 null
的是 Spring
内置的。
应用推断主要在 SpringbootApplication
的构造方法中,this.webApplicationType = WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null)
&& !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null)
&& !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
// 非 web
return NONE;
}
}
// servlet
return SERVLET;
}
}
当然,我们可以直接使用反射调用这个静态方法,判断当前应用环境
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("\t应用类型为: " + deduceFromClasspath.invoke(null));
输出
应用类型为: SERVLET
调用 SpringApplication
对象的 run()
方法时会创建 ApplicationContext
,最后调用 ApplicationContext 的 refresh()
方法完成初始化。
在创建与初始化完成之间的一些拓展功能就由
ApplicationContext
初始化器完成。
在 SpringApplication
的构造方法中,添加的初始化器信息从配置文件中读取:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
当然可以调用 SpringApplication 对象的 addInitializers()
方法添加自定义初始化器:
注意添加初始化器需要在调用
run
方法之前,因为run
方法会refresh
// 初始化器
// 这里用到了函数方法,可以简化代码
spring.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println(">>>>>>>>>>");
if( applicationContext instanceof GenericApplicationContext genericApplicationContext) {
System.out.println(">>>>> 注册 bean3");
genericApplicationContext.registerBean("bean3", Bean3.class);
}
}
});
// 创建并初始化 Spring 容器
ConfigurableApplicationContext context = spring.run(args);
输出
name: bean3 来源: null
name: bean1 来源: class path resource [bean.xml]
name: bean2 来源: com.example.boot.BootApplication
name: beanNameViewResolver 来源: class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]
name: beanNameHandlerMapping 来源: class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]
应用类型为: SERVLET
与添加 ApplicationContext 初始化器一样,在 SpringApplication 的构造方法中,添加的事件监听器信息从配置文件中读取:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 事件监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
可以调用 SpringApplication
对象的 addListeners()
方法添加自定义事件监听器:
spring.addListeners(event -> System.out.println("\t事件为: " + event));
// 创建并初始化 Spring 容器
ConfigurableApplicationContext context = spring.run(args);
输出
2023-12-12 23:08:04.067 INFO 82643 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
事件为: org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@54db056b]
事件为: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
2023-12-12 23:08:04.073 INFO 82643 --- [ main] com.example.boot.BootApplication : Started BootApplication in 0.695 seconds (JVM running for 0.9)
事件为: org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@5119fb47]
事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
事件为: org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@5119fb47]
事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
事件为: org.springframework.context.event.ContextClosedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
依然是在 SpringApplication
的构造方法中,有
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 事件监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 主类推断
this.mainApplicationClass = this.deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
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;
}