源码分析
下面来分析new SpringApplication()
的代码,其源码如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
下面是主要步骤:
-
this.resourceLoader = resourceLoader
:resourceLoader是资源加载器,用于加载资源,如配置文件、类文件等。这里是null,ResourceLoader
的自动配置主要依赖于 Spring Boot 的自动配置机制。 -
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
:源类this.primarySources
通常是包含 Spring Boot 应用的@SpringBootApplication
注解的类或者是定义了一些@Configuration
的类。这些类用于定义 Spring 应用的上下文配置。primarySources
指定的类会被 Spring Boot 扫描,以查找这些BeanDefinition
,也可以说是BeanDefinition
源。 -
this.webApplicationType = WebApplicationType.deduceFromClasspath()
:WebApplicationType
的deduceFromClasspath
静态方法,根据类路径推断应用的Web类型(如是否为Servlet应用、Reactive Web应用或None)。 -
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)
:getSpringFactoriesInstances
方法,从Spring的META-INF/spring.factories
文件中加载所有BootstrapRegistryInitializer
的实现类。BootstrapRegistryInitializer
是一个用于初始化BootstrapRegistry的
回调接口,在使用BootstrapRegistry之前调用它。BootstrapRegistry可以用来注册一些对象,这些对象可以在从Spring Boot启动到Spring容器初始化完成的过程中使用。简单来说,在没有Spring容器之前,可以利用BootstrapRegistry来共享一些对象。 -
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))
:从spring.factories
文件中加载所有ApplicationContextInitializer
的实现类,并将它们设置为ApplicationContext
的初始化器。可以在创建ApplicationContext
和refresh
之间对ApplicationContext
做一些扩展。 -
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))
:从spring.factories
文件中加载所有ApplicationListener
的实现类,并将它们设置为当前对象的事件监听器。 -
this.mainApplicationClass = deduceMainApplicationClass()
:调用deduceMainApplicationClass
方法推断主应用类,主应用类通常是包含main
方法的类,用于启动Spring Boot应用。
注意,创建时,并没有去创建Spring容器,只有run时才会。
步骤演示
primarySources和Sources
这两个主要是定义了BeanDefinition源。primarySources是一开始new时,一般是启动类,主要的BeanDefinition源,也可以在Sources中自己添加。
首先看如下示例:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) {
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
运行报错如下:
Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
这是因为程序根据pom文件的依赖,推断他是一个基于Servlet实现的web服务器,因此需要使用ServletWebServerApplicationContext,从而需要ServletWebServerFactory。
我们给手动加上:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) {
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
再次运行结果如下:
name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalCommonAnnotationProcessor 来源:null
name: org.springframework.context.event.internalEventListenerProcessor 来源:null
name: org.springframework.context.event.internalEventListenerFactory 来源:null
name: testNewSpringApplication 来源:null
name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源:null
name: bean2 来源:com.cys.spring.chapter15.TestNewSpringApplication
name: servletWebServerFactory 来源:com.cys.spring.chapter15.TestNewSpringApplication
上面结果可以看到,一些内置的无法查看到来源,像bean2和servletWebServerFactory都是我们自己定义的,可以分别看到他们的来源。
也可以自己添加source,如下:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) {
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 手动添加一个BeanDefinition源
spring.setSources(new HashSet<String>() {{add("classpath:b01.xml");}});
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
运行结果如下:
应用类型webApplicationType
先直接看源码
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
static WebApplicationType deduceFromClasspath() {
// ClassUtils.isPresent用来判断类路径下是否含有某个类
// 1. 如果包含WEBFLUX_INDICATOR_CLASS,并且不包含WEBMVC_INDICATOR_CLASS和JERSEY_INDICATOR_CLASS,则判断为REACTIVE类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 2. 如果SERVLET_INDICATOR_CLASSES里的都不含有,则判断为NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 3.最后判断为SERVLET类型
return WebApplicationType.SERVLET;
}
主要有以下三步:
- 如果包含了REACTIVE相关的包,不包含servlet相关的包,就判断为REACTIVE类型
- 如果还不包含
javax.servlet.Servlet
和org.springframework.web.context.ConfigurableWebApplicationContext
,则判断为NONE - 否则,判断为SERVLET类型
代码演示:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("1. 演示获取 Bean Definition 源");
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 手动添加一个BeanDefinition源
spring.setSources(new HashSet<String>() {{add("classpath:b01.xml");}});
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
打印如下:
1. 演示获取 Bean Definition 源
2. 演示推断应用类型
应用类型为:SERVLET
setInitializers设置容器初始化器
此步骤用来设置ApplicationContext
的初始化器。ApplicationContext
的初始化器主要用来在创建ApplicationContext
后,refresh
之前对ApplicationContext
做一些扩展。
下面手动添加一个看下:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("1. 演示获取 Bean Definition 源");
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 手动添加一个BeanDefinition源
spring.setSources(new HashSet<String>() {{add("classpath:b01.xml");}});
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
System.out.println("3. 演示 ApplicationContext 初始化器");
spring.addInitializers(applicationContext -> {
if (applicationContext instanceof GenericApplicationContext) {
GenericApplicationContext gac = (GenericApplicationContext) applicationContext;
// 手动注册一个Bean3
gac.registerBean("bean3", Bean3.class);
}
});
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
运行:
发现Bean3被注册进容器。
setListeners设置监听器
ApplicationListener
,即事件监听器。用于监听容器中发布的事件。它是事件驱动模型开发的一部分,允许开发者在特定事件发生时执行相应的操作。
ApplicationListener监听ApplicationEvent及其子事件。通过实现ApplicationListener接口,你可以创建自定义的监听器来监听并响应特定的应用事件。例如,当使用ConfigurableApplicationContext接口中的refresh()、start()、stop()或close()方法时,会发布相应的事件(如ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent和ContextClosedEvent),你可以创建监听器来监听这些事件,并在事件发生时执行必要的操作,如初始化资源、启动服务、清理资源或关闭应用等。
示例:
package com.cys.spring.chapter15;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@Configuration
public class TestNewSpringApplication {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("1. 演示获取 Bean Definition 源");
// 手动new一个SpringApplication,第一个参数就是初始的BeanDefinition源
SpringApplication spring = new SpringApplication(TestNewSpringApplication.class);
// 手动添加一个BeanDefinition源
spring.setSources(new HashSet<String>() {{add("classpath:b01.xml");}});
System.out.println("2. 演示推断应用类型");
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
System.out.println("3. 演示 ApplicationContext 初始化器");
spring.addInitializers(applicationContext -> {
if (applicationContext instanceof GenericApplicationContext) {
GenericApplicationContext gac = (GenericApplicationContext) applicationContext;
// 手动注册一个Bean3
gac.registerBean("bean3", Bean3.class);
}
});
System.out.println("4. 演示监听器与事件");
// 添加一个监听器监听所有事件
spring.addListeners(event -> System.out.println("\t事件为:" + event.getClass()));
// 调用run方法,返回一个容器
ConfigurableApplicationContext context = spring.run(args);
// 打印容器里的BeanDefinition
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
结果打印出很多时间,如下:
1. 演示获取 Bean Definition 源
2. 演示推断应用类型
应用类型为:SERVLET
3. 演示 ApplicationContext 初始化器
4. 演示监听器与事件
事件为:class org.springframework.boot.context.event.ApplicationStartingEvent
事件为:class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.13)
事件为:class org.springframework.boot.context.event.ApplicationContextInitializedEvent
2024-03-10 12:25:54.085 INFO 50655 --- [ main] c.c.s.c.TestNewSpringApplication : Starting TestNewSpringApplication using Java 11.0.2 on testdeMBP with PID 50655 (/Users/fltech/Desktop/cys/学习笔记/sping高级-heima/sping-advanced/target/classes started by test in /Users/fltech/Desktop/cys/学习笔记/sping高级-heima/sping-advanced)
2024-03-10 12:25:54.092 INFO 50655 --- [ main] c.c.s.c.TestNewSpringApplication : No active profile set, falling back to 1 default profile: "default"
事件为:class org.springframework.boot.context.event.ApplicationPreparedEvent
2024-03-10 12:25:54.960 INFO 50655 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2024-03-10 12:25:54.973 INFO 50655 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-03-10 12:25:54.973 INFO 50655 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.68]
2024-03-10 12:25:55.226 INFO 50655 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-03-10 12:25:55.227 INFO 50655 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 811 ms
2024-03-10 12:25:55.313 INFO 50655 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
事件为:class org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
事件为:class org.springframework.context.event.ContextRefreshedEvent
2024-03-10 12:25:55.331 INFO 50655 --- [ main] c.c.s.c.TestNewSpringApplication : Started TestNewSpringApplication in 2.021 seconds (JVM running for 2.673)
事件为:class org.springframework.boot.context.event.ApplicationStartedEvent
事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
事件为:class org.springframework.boot.context.event.ApplicationReadyEvent
事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalCommonAnnotationProcessor 来源:null
name: org.springframework.context.event.internalEventListenerProcessor 来源:null
name: org.springframework.context.event.internalEventListenerFactory 来源:null
name: bean3 来源:null
name: testNewSpringApplication 来源:null
name: bean1 来源:class path resource [b01.xml]
name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源:null
name: bean2 来源:com.cys.spring.chapter15.TestNewSpringApplication
name: servletWebServerFactory 来源:com.cys.spring.chapter15.TestNewSpringApplication
事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
事件为:class org.springframework.context.event.ContextClosedEvent
主类推断
主类推断即用来推断含有main方法的启动Spring Boot应用的主类。
可使用反射查看
Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
deduceMainApplicationClass.setAccessible(true);
System.out.println("\t主类是:"+deduceMainApplicationClass.invoke(spring))