通过执行SpringApplication
的静态run()
方法,可以完成SpringBoot应用的启动。本文对SpringApplication
的实例化过程进行分析。
查看SpringApplication#run
方法,可以看到,实际上就是new
了一个SpringApplication
对象,参数primarySources
即为入口类:
根据上面的分析,启动类也可以实现如下:
@SpringBootApplication
public class SourceDemoApplication {
public static void main(String[] args) {
new SpringApplication(SourceDemoApplication.class).run(args);
}
}
如下图,这种方式可以在启动应用前进行一些设置,如设置Banner
、添加初始化操作等。
通过构造方法进行初始化:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
// 参数resourceLoader:资源加载接口,默认使用DefaultResourceLoader,
// 例如指定banner信息文件路径,默认为classpath下,banner.*文件
// 参数primarySources:默认为入口类,只要直接或间接配置了@EnableAutoConfiguration注解的类均可
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// 入口类不能为空
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将入口类参数转换成LinkedHashSet,去重,赋值给成员变量
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载并初始化ApplicationContextInitializer及其相关实现类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载并初始化ApplicationListener及其相关实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断main方法Class类
this.mainApplicationClass = deduceMainApplicationClass();
}
// 基于classpath下是否存在类进行类型推断
static WebApplicationType deduceFromClasspath() {
// DispatcherHandler存在,DispatcherServlet和ServletContainer不存在,为REACTIVE类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// Servlet和ConfigurableWebApplicationContext任一个不存在,不是web应用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 否则为SERVLET类型
return WebApplicationType.SERVLET;
}
此处核心是使用ClassUtils#isPresent
方法通过反射创建指定类,创建成功则存在,否则不存在。
ApplicationContextInitializer
是Spring IOC容器提供的一个接口,只定义了一个initialize(C applicationContext)
方法,用于初始化应用上下文。
可以看到,ApplicationContextInitializer
的加载包括两步:
getSpringFactoriesInstances
获取相关实例setInitializers
设置实例getSpringFactoriesInstances
获取相关实例:SpringFactoriesLoader.loadFactoryNames
方法去获取META-INF/spring.factories
配置文件中,属性org.springframework.context.ApplicationContextInitializer
的值,值均是ApplicationContextInitializer
的实现类。获取到初始化器全限定名后,通过createSpringFactoriesInstances
方法进行ApplicationContextInitializer
的实例化:
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
// 遍历initializer全类名
for (String name : names) {
try {
// 获取Class
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 判断类型合法性
Assert.isAssignable(type, instanceClass);
// 获取有参构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 创建对象
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
由此便变成了初始化器ApplicationContextInitializer
的实例化,然后对创建的bean进行排序,最后完成initializers对象的设置。
// 此处通过new ArrayList给this.initializers赋值了一个新创建的list
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
首先自定义一个ApplicationContextInitializer
实现类
自定义初始化器生效的方式包括以下三种:
spring.factories
文件的org.springframework.context.ApplicationContextInitializer
属性值添加该类的全限定名;application.properties
等配置文件中指定;run
方法之前通过SpringApplication#addInitializers
方法添加初始化器ApplicationListener
加载过程与ApplicationContextInitializer
完全一致:
getSpringFactoriesInstances
获取相关实例setListeners
设置实例getSpringFactoriesInstances
中还是通过SpringFactoriesLoader.loadFactoryNames
获取spring.factories
中ApplicationListener
属性的值,然后创建对象,并进行排序,赋值。Spring的事件传播机制是基于观察者模式实现的,例如,在ApplicationContext
管理Bean的生命周期过程中,将一些改变定义为事件ApplicationEvent
,ApplicationContext
通过ApplicationListener
监听ApplicationEvent
,事件被发布时,ApplicationListener
进行相应的处理。也就是ApplicationListener
和ApplicationEvent
配置使用,可以实现ApplicationContext
的事件处理。容器中存在ApplicationListener
的对象时,当ApplicationListener
调用publishEvent
方法时,对应Bean被触发。
ApplicationListener
接口只有一个onApplicationEvent
方法,用于处理事件,Event
是ApplicationEvent
的具体实现,即具体的事件。
当ApplicationContext
被初始化或刷新时,会触发ContextRefreshedEvent
事件,通过如下方式可监听:
要注意的是,ApplicationListener
需要被注册成Bean对象。
SpringApplication#deduceMainApplicationClass
private Class<?> deduceMainApplicationClass() {
try {
// 创建运行时异常,获取栈元素数组
// 通过 new RuntimeException().getStackTrace() 获取信息,
// 与Thread.currentThread().getStackTrace()相比,效率更高,性能开销较小
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 如果包含方法main,则通过Class.forName创建对象并返回,
// 最后赋值给SpringApplication的mainApplicationClass属性
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// 忽略异常,返回null
// Swallow and continue
}
return null;
}