前言
本文涉及代码所使用的SpringBoot版本为2.6.3(这是截至此文发布时Spring Cloud Alibaba最新版本适配的SpringBoot版本,点击此处可查看Spring Cloud Alibaba与SpringBoot的版本适配情况),如果未来版本的SpringBoot源码有较大变动,不建议再学习本文。
学习SpringBoot源码的入口点
@SpringBootApplication
public class TestMain {
public static void main(String[] args) {
SpringApplication.run(TestMain.class, args);
}
}
上面这段代码相信使用SpringBoot的大家再熟悉不过了,是SpringBoot的应用程序启动入口,这也是SpringApplication(org.springframework.boot.SpringApplication)的最主要作用。SpringApplication.run方法是本系列文章讲解源码的入口点,接下来我们就来一层一层深挖SpringAppliaction.run背后到底做了什么能让我们的SpringBoot应用正常启动并提供服务。
我们先来看一下SpringApplication的类层次性结构。(这是IDEA的类层次性结构视图,默认快捷键Ctrl+H)
可以看到SpringApplication没有继承除Object以外的任何类也没有实现任何接口,不需要考虑复杂的类层次性关系,这也是其适合作为学习SpringBoot源码入口点的主要原因。
SpringApplication的构造方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
上面就是SpringApplication的run方法源码(run方法到底做了什么以及run方法返回的ConfigurableApplicationContext是什么会在以后文章中详解,因为要理解这些需要依赖后面讲的源码。本文的关注点在SpringApplication的构造方法),可以看到最终的run方法调用了构造方法new SpringApplication(primarySources)。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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();
}
构造方法SpringApplication(Class<?>... primarySources)最终调用SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)。下面来看SpringApplication构造方法每步代码的作用。
初识ResourceLoader、Resource
SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)(由于传入的是ResourceLoader是null,在构造函数中并看不出具体作用,所以本文先初步了解接口方法,后面的文章会讲解ResourceLoader的实现类源码)
ResourceLoader(org.springframework.core.io.ResourceLoader)是用于加载资源(例如,类路径或文件系统资源)的策略接口。
public interface ResourceLoader {
/** 从类路径加载的伪 URL 前缀:“classpath:” */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* 返回指定资源位置的Resource句柄。
*/
Resource getResource(String location);
/**
* 返回此ResourceLoader使用的ClassLoader 。
*/
@Nullable
ClassLoader getClassLoader();
}
ResourceLoader的层次性关系(子类型):
Resource(org.springframework.core.io.Resource) 是从底层资源的实际类型(例如文件或类路径资源)中抽象出来的资源描述符的接口。
public interface Resource extends InputStreamSource {
/**
* 确定此资源是否实际以物理形式存在
*/
boolean exists();
/**
* 指示是否可以通过getInputStream()读取此资源的非空内容
*/
default boolean isReadable() {
return exists();
}
/**
* 指示此资源是否表示具有打开流的句柄。
*/
default boolean isOpen() {
return false;
}
/**
* 确定此资源是否代表文件系统中的文件。
*/
default boolean isFile() {
return false;
}
/**
* 返回此资源的 URL 句柄。
*/
URL getURL() throws IOException;
/**
* 返回此资源的 URI 句柄。
*/
URI getURI() throws IOException;
/**
* 返回此资源的文件句柄。
*/
File getFile() throws IOException;
/**
* 返回一个ReadableByteChannel 。
*/
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 确定此资源的内容长度。
*/
long contentLength() throws IOException;
/**
* 确定此资源的最后修改时间戳。
*/
long lastModified() throws IOException;
/**
* 创建与此资源相关的资源。
*/
Resource createRelative(String relativePath) throws IOException;
/**
* 确定此资源的文件名,即通常是路径的最后部分:例如,“myfile.txt”。
* 如果这种类型的资源没有文件名,则返回null 。
*/
@Nullable
String getFilename();
/**
* 返回此资源的描述,用于在使用资源时输出错误。
*/
String getDescription();
}
Resource的层次性关系(父类型):
Resource的层次性关系(子类型):
WebApplicationType枚举
SpringApplication的构造方法:
WebApplicationType(org.springframework.boot.WebApplicationType)列出了可能的 Web 应用程序类型的枚举:NONE、SERVLET、REACTIVE
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
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";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
NONE:该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
SERVLET:该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。
REACTIVE:该应用程序应作为响应式Web应用程序运行,并应启动嵌入式响应式Web服务器。
WebApplicationType的deduceFromClasspath()方法实现了从类路径推断出Web应用程序类型的功能
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
ClassUtils(org.springframework.util.ClassUtils)是杂项java.lang.Class实用程序方法,主要供Spring框架内部使用。
ClassUtils的isPresent(String className, @Nullable ClassLoader classLoader)方法可以通过传入类的全名和类加载器来判断该类是否被类加载器所加载(更多的ClassUtils源码将在SpringBoot源码支线专栏文章中进行详解。)
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}
回到WebApplicationType的deduceFromClasspath()方法
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
再结合以下字段
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";
不难看出应用引入的依赖会决定WebApplicationType类型,可以推出如下表格:
应用引入的依赖 | 包含推断的类 | WebApplicationType |
---|---|---|
不引入web服务器 | - | NONE |
spring-boot-starter-web | SERVLET_INDICATOR_CLASSES WEBMVC_INDICATOR_CLASS |
SERVLET |
spring-boot-starter-webflux | WEBFLUX_INDICATOR_CLASS | REACTIVE |
spring-boot-starter-web和 spring-boot-starter-webflux |
SERVLET_INDICATOR_CLASSES WEBMVC_INDICATOR_CLASS WEBFLUX_INDICATOR_CLASS |
SERVLET |
初识BootstrapRegistryInitializer、BootstrapRegistry
SpringApplication的构造方法:
BootstrapRegistryInitializer是在使用BootstrapRegistry之前对其进行初始化的回调接口(这是一个函数式接口)
@FunctionalInterface
public interface BootstrapRegistryInitializer {
/**
* 使用任何所需的registrations初始化给定的BootstrapRegistry 。
* @param 要初始化的BootstrapRegistry
*/
void initialize(BootstrapRegistry registry);
}
BootstrapRegistry是一个简单的对象注册表,在启动和Environment后处理期间可用,直到ApplicationContext准备好为止。(具体实现类源码以及相关服务对象会在本专栏以后的文章中会详解)
public interface BootstrapRegistry {
/**
* 向注册表注册给定类型。如果给定的类型已经注册过并且没有作为singleton获取,则会被替换。
* @param type 实例类型
* @param instanceSupplier 实例供应者
*/
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* 如果给定类型不存在,则向注册表注册该给定类型。
* @param type 实例类型
* @param instanceSupplier 实例供应者
*/
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* 判断给定的类型是否存在。
*/
<T> boolean isRegistered(Class<T> type);
/**
* 返回给定类型的任何现有BootstrapRegistry.InstanceSupplier
*/
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
/**
* 添加一个ApplicationListener,当BootstrapContext关闭并且ApplicationContext已准备好时,
* 它将使用BootstrapContextClosedEvent调用。
*/
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
/**
* 在需要时提供实际实例的供应者
* @param <T> 实例类型
*/
@FunctionalInterface
interface InstanceSupplier<T> {
/**
* 工厂方法用于在需要时创建实例。
* @param context BootstrapContext可用于获取其他引导程序实例
* @return 实例
*/
T get(BootstrapContext context);
/**
* 返回提供的实例的作用域。
* @since 2.4.2
*/
default Scope getScope() {
return Scope.SINGLETON;
}
/**
* 返回一个带有更新的BootstrapRegistry.Scope的BootstrapRegistry.InstanceSupplier
* @param scope 新的作用域
* @since 2.4.2
*/
default InstanceSupplier<T> withScope(Scope scope) {
Assert.notNull(scope, "Scope must not be null");
InstanceSupplier<T> parent = this;
return new InstanceSupplier<T>() {
@Override
public T get(BootstrapContext context) {
return parent.get(context);
}
@Override
public Scope getScope() {
return scope;
}
};
}
/**
* 工厂方法用于为给定实例创建BootstrapRegistry.InstanceSupplier
* @param <T> 实例类型
* @param instance 实例
*/
static <T> InstanceSupplier<T> of(T instance) {
return (registry) -> instance;
}
/**
* 工厂方法用于从Supplier创建BootstrapRegistry.InstanceSupplier
* @param <T> 实例类型
* @param supplier 提供实例的供应者
*/
static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
return (registry) -> (supplier != null) ? supplier.get() : null;
}
}
/**
* 实例的作用域
* @since 2.4.2
*/
enum Scope {
/**
* 单例实例。BootstrapRegistry.InstanceSupplier只会被调用一次,并且每次都会返回相同的实例
*/
SINGLETON,
/**
* 原型实例。每当需要实例时,都会调用BootstrapRegistry.InstanceSupplier。
*/
PROTOTYPE
}
BootstrapRegistry的层次性关系(子类型)
初识getSpringFactoriesInstances方法
SpringApplication的构造方法:
SpringApplication的getSpringFactoriesInstances方法提供了从应用的所有JAR包中的META-INF/spring.factories文件加载和实例化给定类型接口的实现类
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 使用名称并确保名称唯一以防止重复
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringApplication的getSpringFactoriesInstances方法很重要,关于SpringApplication是如何对所有JAR包中的META-INF/spring.factories文件进行访问涉及ClassLoader的资源访问机制和SpringFactoriesLoader对资源的缓存处理,如果放在此文中详解会导致篇幅过长,所以此专栏的第二章将对getSpringFactoriesInstances方法进行详解。
下面直接验证getSpringFactoriesInstances方法的调用成果,首先我的应用引入了如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
然后按条件查找spring.factories。(spring.factories文件必须是Properties格式,其中key是接口或抽象类的完全限定名称,value是逗号分隔的实现类名称列表。例如:example.MyService = example.MyServiceImpl1, example.MyServiceImpl2(example.MyService是接口的名称, MyServiceImpl1和MyServiceImpl2是两个实现))
SpringApplication的构造方法会通过getSpringFactoriesInstances方法对类成员字段bootstrapRegistryInitializers、initializers进行赋值(会分别去查找META-INF/spring.factories中的BootstrapRegistryInitializer.class和ApplicationContextInitializer.class的实现类),所以我在这里搜索module里key为org.springframework.boot.BootstrapRegistryInitializer的spring.factories文件:
然而并没有搜索到任何结果。。。接着搜索module里key为org.springframework.context.ApplicationContextInitializer的spring.factories文件
发现有两个jar包内有满足条件的spring.factories,分别打开它们的spring.factories文件。
这是spring-boot-autoconfigure-2.6.3.jar的spring.factories文件("\"表示不换行):
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
这是spring-boot-2.6.3.jar的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
我将debug断点打在了SpringApplication构造方法的最后一行,我们直接来看bootstrapRegistryInitializers、initializers这两个成员字段的内容:
由于应用依赖的JAR包中没有包含key是org.springframework.boot.BootstrapRegistryInitializer的spring.factories文件,所以bootstrapRegistryInitializers的size为0。
initializers里的内容就是刚才我所搜索到的两个spring.factories里key是org.springframework.context.ApplicationContextInitializer的value,而且数量也是一致的。
初识ApplicationContextInitializer、ConfigurableApplicationContext
SpringApplication的构造方法:
SpringApplication的setInitializers方法:
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
ApplicationContextInitializer(org.springframework.context.ApplicationContextInitializer)是用于在刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(通常在需要对应用程序上下文进行一些编程初始化的Web应用程序中使用。例如,针对上下文环境注册属性源或激活配置文件。)
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* 初始化给定的应用程序上下文。
* @param applicationContext 要配置的应用程序
*/
void initialize(C applicationContext);
}
ApplicationContextInitializer的层次性关系(子类型):
ConfigurableApplicationContext(org.springframework.context.ConfigurableApplicationContext)用于封装配置和生命周期方法
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
/**
* 任意数量的这些字符都被视为单个字符串值中多个上下文配置路径之间的分隔符
*/
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
* 工厂中 ConversionService bean 的名称。如果未提供,则应用默认转换规则。
*/
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
/**
* 工厂中 LoadTimeWeaver bean 的名称。
* 如果提供了这样的 bean,上下文将使用临时 ClassLoader 进行类型匹配,以允许 LoadTimeWeaver 处理所有实际的 bean 类。
*/
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
/**
* 工厂中Environment bean 的名称。
*/
String ENVIRONMENT_BEAN_NAME = "environment";
/**
* 工厂中系统属性 bean 的名称。
*/
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
/**
* 工厂中系统环境 bean 的名称。
*/
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
/**
* 工厂中ApplicationStartup bean 的名称。
*/
String APPLICATION_STARTUP_BEAN_NAME = "applicationStartup";
/**
* 关闭挂钩线程的名称:“SpringContextShutdownHook”。
*/
String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";
/**
* 设置此应用程序上下文的唯一 ID。
*/
void setId(String id);
/**
* 设置此应用程序上下文的父级。
*/
void setParent(@Nullable ApplicationContext parent);
/**
* 为此应用程序上下文设置Environment。
* @since 3.1
*/
void setEnvironment(ConfigurableEnvironment environment);
/**
* 以可配置的形式返回此应用程序上下文的Environment ,允许进一步定制。
*/
@Override
ConfigurableEnvironment getEnvironment();
/**
* 为此应用程序上下文设置ApplicationStartup 。
*/
void setApplicationStartup(ApplicationStartup applicationStartup);
/**
* 返回此ApplicationStartup程序上下文的 ApplicationStartup。
*/
ApplicationStartup getApplicationStartup();
/**
* 添加一个新的 BeanFactoryPostProcessor,
* 它将在刷新时应用于此应用程序上下文的内部 bean 工厂,然后再评估任何 bean 定义。
* 在上下文配置期间调用。
*/
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
/**
* 添加一个新的 ApplicationListener,它将在上下文刷新和上下文关闭等上下文事件上得到通知。
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 指定 ClassLoader 来加载类路径资源和 bean 类。
* 这个上下文类加载器将被传递到内部 bean 工厂。
*/
void setClassLoader(ClassLoader classLoader);
/**
* 使用此应用程序上下文注册给定的协议解析器,允许处理其他资源协议。
* 任何此类解析器都将在此上下文的标准解析规则之前调用。因此,它也可以覆盖任何默认规则。
*/
void addProtocolResolver(ProtocolResolver resolver);
/**
* 加载或刷新配置的持久表示,它可能来自基于 Java 的配置、XML 文件、属性文件、关系数据库模式或其他格式。
* 由于这是一种启动方法,它应该在失败时销毁已经创建的单例,以避免悬空资源。
* 换句话说,在调用此方法之后,应该实例化所有或根本不实例化单例。
*/
void refresh() throws BeansException, IllegalStateException;
/**
* 向 JVM 运行时注册一个关闭挂钩,在 JVM 关闭时关闭此上下文,除非它当时已经关闭。
* 该方法可以多次调用。每个上下文实例只会注册一个关闭挂钩(最多)。
*/
void registerShutdownHook();
/**
* 关闭此应用程序上下文,释放实现可能持有的所有资源和锁。这包括销毁所有缓存的单例 bean。
* 注意:不在父上下文中调用close ;父上下文有自己独立的生命周期。
* 此方法可以多次调用而不会产生副作用:对已关闭上下文的后续close调用将被忽略
*/
@Override
void close();
/**
* 判断此应用上下文是否处于活动状态,即是否至少刷新过一次且尚未关闭。
*/
boolean isActive();
/**
* 返回此应用程序上下文的内部 bean 工厂。可用于访问底层工厂的特定功能。
* 注意:不要用这个对bean工厂进行后处理;单例之前已经实例化过。
* 使用 BeanFactoryPostProcessor 在接触 bean 之前拦截 BeanFactory 设置过程。
* 通常,只有在上下文处于活动状态时才能访问此内部工厂,即在refresh()和close()之间。
* isActive()标志可用于检查上下文是否处于适当的状态。
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
ConfigurableApplicationContext的层次性关系(父类型):
ConfigurableApplicationContext的层次性关系(子类型):
初识ApplicationListener、ApplicationEvent、PayloadApplicationEvent
SpringApplication的构造方法:
SpringApplication的setListeners方法:
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
ApplicationListener(org.springframework.context.ApplicationListener)是由应用程序事件监听器实现的接口,也是基于观察者设计模式的标准java.util.EventListener接口。从Spring 3.0开始, ApplicationListener可以一般性地声明它感兴趣的事件类型。当使用Spring ApplicationContext注册时,事件将被相应地过滤,只有匹配事件对象才会调用监听器。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 处理应用程序事件。
* @param event 要响应的事件
* @param <E> 要监听的特定ApplicationEvent子类
*/
void onApplicationEvent(E event);
/**
* 为给定的有效负载使用者创建一个新的ApplicationListener 。
* @param consumer 事件有效负载消费者
* @param <T> 事件负载的类型
*/
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return event -> consumer.accept(event.getPayload());
}
}
ApplicationListener的层次性关系(父类型):
ApplicationListener的层次性关系(子类型):
ApplicationEvent(org.springframework.context.ApplicationEvent)可被所有应用程序扩展
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
/** 事件发生的系统时间。*/
private final long timestamp;
/**
* 创建一个新的ApplicationEvent并将其timestamp设置为System.currentTimeMillis()。
* @param source 事件最初发生的对象或与事件相关联的对象(从不为null)
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* 创建一个新的ApplicationEvent ,其timestamp设置为提供的Clock中的Clock.millis()返回的值。
* 此构造函数通常用于测试场景。
* @param source 事件最初发生的对象或与事件相关联的对象(从不为null)
* @param clock 提供时间戳的时钟
*/
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
/**
* 返回事件发生的时间(以毫秒为单位)
*/
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEvent的层次性关系
PayloadApplicationEvent(org.springframework.context.PayloadApplicationEvent)是携带任意有效负载的ApplicationEvent 。
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
private final T payload;
/**
* 创建一个新的 PayloadApplicationEvent。
* @param source 最初发生事件的对象(从不为null)
* @param payload 有效负载对象(从不为null)
* @param <T> 事件的payload类型
*/
public PayloadApplicationEvent(Object source, T payload) {
super(source);
Assert.notNull(payload, "Payload must not be null");
this.payload = payload;
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload()));
}
/**
* 返回事件的有效负载。
*/
public T getPayload() {
return this.payload;
}
}
PayloadApplicationEvent的层次性关系:
deduceMainApplicationClass方法
SpringApplication构造方法的最后一步:
SpringApplication的deduceMainApplicationClass方法:
private Class<?> deduceMainApplicationClass() {
try {
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;
}
该方法用于推断并返回主应用类,主应用类就是运行SpringApplication.run代码的类,也是应用程序入口的main方法所在类。通过RuntimeException的getStackTrace()获得当前方法的堆栈跟踪,并遍历此堆栈跟踪,返回方法名称是main的类。
构造方法执行完毕后SpringApplication的mainApplicationClass: