SpringBoot源码学习系列——构造流程分析
通过执行SpringApplication
的静态run()
方法,可以完成SpringBoot应用的启动。本文对SpringApplication
的实例化过程进行分析。
SpringApplication初始化简介
查看SpringApplication#run
方法,可以看到,实际上就是new
了一个SpringApplication
对象,参数primarySources
即为入口类:
根据上面的分析,启动类也可以实现如下:
@SpringBootApplication
public class SourceDemoApplication {public static void main(String[] args) {new SpringApplication(SourceDemoApplication.class).run(args);}}
如下图,这种方式可以在启动应用前进行一些设置,如设置Banner
、添加初始化操作等。
SpringApplication实例化流程
整体流程
通过构造方法进行初始化:
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();}
web应用类型推断
// 基于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加载
源码分析
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 {// 获取ClassClass<?> 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赋值了一个新创建的listpublic void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {this.initializers = new ArrayList<>(initializers);}
自定义Initializer示例
首先自定义一个ApplicationContextInitializer
实现类
自定义初始化器生效的方式包括以下三种:
- 与源码类似,在
spring.factories
文件的org.springframework.context.ApplicationContextInitializer
属性值添加该类的全限定名; - 在
application.properties
等配置文件中指定; - 在执行
run
方法之前通过SpringApplication#addInitializers
方法添加初始化器
ApplicationListener加载
源码分析
ApplicationListener
加载过程与ApplicationContextInitializer
完全一致:
- 通过
getSpringFactoriesInstances
获取相关实例 - 通过
setListeners
设置实例
getSpringFactoriesInstances
中还是通过SpringFactoriesLoader.loadFactoryNames
获取spring.factories
中ApplicationListener
属性的值,然后创建对象,并进行排序,赋值。
自定义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;}