SpringBoot源码学习系列——构造流程分析

通过执行SpringApplication的静态run()方法,可以完成SpringBoot应用的启动。本文对SpringApplication的实例化过程进行分析。

SpringApplication初始化简介

SpringBoot源码学习系列——构造流程分析_第1张图片
查看SpringApplication#run方法,可以看到,实际上就是new了一个SpringApplication对象,参数primarySources即为入口类:
SpringBoot源码学习系列——构造流程分析_第2张图片
在这里插入图片描述
根据上面的分析,启动类也可以实现如下:

@SpringBootApplication
public class SourceDemoApplication {

    public static void main(String[] args) {
        new SpringApplication(SourceDemoApplication.class).run(args);
    }

}

如下图,这种方式可以在启动应用前进行一些设置,如设置Banner、添加初始化操作等。
SpringBoot源码学习系列——构造流程分析_第3张图片

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应用类型推断

SpringBoot源码学习系列——构造流程分析_第4张图片

    // 基于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)方法,用于初始化应用上下文。
SpringBoot源码学习系列——构造流程分析_第5张图片
可以看到,ApplicationContextInitializer的加载包括两步:

  1. 通过getSpringFactoriesInstances获取相关实例
  2. 通过setInitializers设置实例
    首先看下通过getSpringFactoriesInstances获取相关实例:
    SpringBoot源码学习系列——构造流程分析_第6张图片
    可以看到,还是首先通过SpringFactoriesLoader.loadFactoryNames方法去获取META-INF/spring.factories配置文件中,属性org.springframework.context.ApplicationContextInitializer的值,值均是ApplicationContextInitializer的实现类。
    SpringBoot源码学习系列——构造流程分析_第7张图片

获取到初始化器全限定名后,通过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);
	}

自定义Initializer示例

首先自定义一个ApplicationContextInitializer实现类
SpringBoot源码学习系列——构造流程分析_第8张图片
自定义初始化器生效的方式包括以下三种:

  1. 与源码类似,在spring.factories文件的org.springframework.context.ApplicationContextInitializer属性值添加该类的全限定名;
  2. application.properties等配置文件中指定;
  3. 在执行run方法之前通过SpringApplication#addInitializers方法添加初始化器
    SpringBoot源码学习系列——构造流程分析_第9张图片

ApplicationListener加载

源码分析

ApplicationListener加载过程与ApplicationContextInitializer完全一致:
SpringBoot源码学习系列——构造流程分析_第10张图片

  1. 通过getSpringFactoriesInstances获取相关实例
  2. 通过setListeners设置实例
    getSpringFactoriesInstances中还是通过SpringFactoriesLoader.loadFactoryNames获取spring.factoriesApplicationListener属性的值,然后创建对象,并进行排序,赋值。
    SpringBoot源码学习系列——构造流程分析_第11张图片

自定义ApplicationListener示例

Spring的事件传播机制是基于观察者模式实现的,例如,在ApplicationContext管理Bean的生命周期过程中,将一些改变定义为事件ApplicationEvent,ApplicationContext通过ApplicationListener监听ApplicationEvent,事件被发布时,ApplicationListener进行相应的处理。也就是ApplicationListenerApplicationEvent配置使用,可以实现ApplicationContext的事件处理。容器中存在ApplicationListener的对象时,当ApplicationListener调用publishEvent方法时,对应Bean被触发。

SpringBoot源码学习系列——构造流程分析_第12张图片
ApplicationListener接口只有一个onApplicationEvent方法,用于处理事件,EventApplicationEvent的具体实现,即具体的事件。

ApplicationContext被初始化或刷新时,会触发ContextRefreshedEvent事件,通过如下方式可监听:
SpringBoot源码学习系列——构造流程分析_第13张图片
要注意的是,ApplicationListener需要被注册成Bean对象。

入口类推断

SpringBoot源码学习系列——构造流程分析_第14张图片
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;
   }

你可能感兴趣的:(spring,boot,学习,java)