SpringBoot编程思想 读书笔记 -- SpringApplication

《SpringBoot编程思想》书中关于SpringBoot2源码分析,版本使用的是:2.0.2.RELEASE
而本文中的源码分析,版本使用的是:2.1.6.RELEASE,所以与书中会有少许差异;

1. SpringApplication 初始化阶段

分为 构造阶段配置阶段

1.1 SpringApplication 构造阶段

平时我们使用的都是调用 SpringApplication 的 run() 方法,其实内部是包含了其构造方法的;

日常使用 SpringApplication 的案例:

@SpringBootApplication
public class MicroVideoApplication{
    public static void main(String[] args) {
        SpringApplication.run(MicroVideoApplication.class, args);
    }
}

其 SpringApplication 内部 run() 方法的源码是:

	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 的构造方法的,返回的是一个 ApplicationContext ;


上诉日常用法中 SpringApplication.run(MicroVideoApplication.class, args) ,其中 MicroVideoApplication 类其实可以更换成任意其他类,只要此类上也使用 @SpringBootApplication 进行注解;如下所示:

public class MicroVideoApplication {

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

@SpringBootApplication
class BootStrapClass {
}

SpringApplication 构造过程:

	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();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

根据其调用链路依次为:推断 Web 应用类型加载 Spring 应用上下文初始化器加载 Spring 应用事件监听器推导应用引导类

1.1.1. 推断 Web 应用类型

deduceFromClasspath()

public enum WebApplicationType {

	/**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.
	 */
	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";

	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;
	}

    // ...
}

deduceFromClasspath() 利用 ClassUtils#isPresent 方法依次判断 DispatcherHandler 、 ConfigurableWebApplicationContext 、 Servlet 和 DispatcherServlet 以及 ServletContainer 的存在性组合情况,从而推断 Web 应用类型:
(1)当 DispatcherHandler 存在,且 DispatcherServlet 和 ServletContainer 都不存在时;
推断出类型为:WebApplicationType.REACTIVE,即 Reactive Web ;

(2)当 Servlet 和 ConfigurableWebApplicationContext 均不存在时;
推断出应用类型为:WebApplicationType.NONE,即 非 Web 应用;

(3)否则,类型就为:WebApplicationType.SERVLET,即 普通的 Web 应用;

1.1.2. 加载 Spring 应用上下文初始化器

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

分为两个动作:getSpringFactoriesInstances(Class) 和 setInitializers(Collection) ;

    // 第一步
	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();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

    // 第二步
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<>();
		this.initializers.addAll(initializers);
	}

第一步中,将返回所有 META-INF/spring.factories 资源中配置的 ApplicationContextInitializer 实现类名单;

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

在这里插入图片描述

当获取到 ApplicationContextInitializer 实现类名单后,调用 createSpringFactoriesInstances 方法初始化这些实现类;

	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				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 实现类必须存在默认构造器;

1.1.3. 加载 Spring 应用事件监听器

本过程的实现手段与“加载 Spring 应用上下文初始化器”基本一致,先执行 getSpringFactoriesInstances 方法,再设置实例集合,只不过初始化的对象类型从 ApplicationContextInitializer 变成了 ApplicationListener

	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();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

1.1.4. 推导应用引导类

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;
	}

该方法根据当前线程执行栈来判断栈中哪个类包含main方法,尽管这个方法的实现不够严谨,不过可以覆盖绝大多数以 Java 的标准 mian 方法引导的情况;


在 SpringApplication 的构造过程中, SpringApplication 的属性 Set<Class<?>> primarySources WebApplicationType webApplicationType 、 List

1.2 SpringApplication 配置阶段

构造阶段完成后,接下来是配置阶段;该阶段是可选的,不设置则会使用默认配置;

该阶段主要是用于调整或补充构造阶段的状态;

调整行为主要以 SpringApplication Setter 方法为代表,补充行为则以 add* 方法为主;可参考文档 23.3 Customizing SpringApplication

也可以使用 SpringApplicationBuilder ,作用相同,主要是提高 API 的便利性。

1.2.1 调整 SpringApplication 设置

官网文档案例:

public static void main(String[] args) {
	SpringApplication app = new SpringApplication(MySpringConfiguration.class);
	app.setBannerMode(Banner.Mode.OFF);
	app.run(args);
}

public static void main(String[] args) {
    new SpringApplicationBuilder()
		.sources(Parent.class)
		.child(Application.class)
		.bannerMode(Banner.Mode.OFF)
		.run(args);
}

1.2.2 增加 SpringApplication 配置源

1.2.3 调整 SpringBoot 外部化配置


2. SpringApplication 运行阶段

2.1 SpringApplication 准备阶段

2.2 Spring 应用上下文启动阶段

2.3 Spring 应用上下文启动后阶段

3. SpringApplication 结束阶段

3.1 SpringApplication 正常结束

3.2 SpringApplication 异常结束

4. SpringApplication 应用退出

4.1 SpringBoot 应用正常退出

4.2 SpringBoot 应用异常退出


版权声明:本文为u013837825原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。