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 初始化器
- 添加事件监听器
- 主类推断
获取 Bean Definition 源
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(); } }
<?xml version="1.0" encoding="UTF-8"?> <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
添加 ApplicationContext 初始化器
调用 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; }