目录
2、使用类加载器加载ApplicationContextInitializer和ApplicationListener
二、SpringApplication的run方法(主要流程节点)
一、构造器初始化和run之前可以调整的配置
Spring Boot在执行SpringApplication的run方法之前,其实在构造函数中已经完成了很多初始化的准备操作,并且在run方法之前我们可以配置很多信息(上一篇博客中分析了很多的字段都是有Setter的)。比如我们可以将代码写作如下:
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(KevinToolApplication.class);
springApplication.setWebApplicationType(WebApplicationType.SERVLET);
springApplication.run(args);
}
先看看全参构造器本身都初始化了哪些组件或者配置:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 确定当前的ApplicationContext类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 确定main方法所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
一般情况下类加载器为null,main方法所在的类则会传入(比如当前为KevinToolApplication.class)。
1、确定当前ApplicationContext类型
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;
}
现在是几个基础判断,后续可以通过Settter进行修改,最好根据createApplicationContext方法映射ApplicationContext类型。
1)、如果有DispatcherHandler,没有DispatcherServlet和ServletContainer则创建REACTIVE类型。
2)、如果没有ConfigurableWebApplicationContext类型,则创建NONE(非Web类型)。
3)、否则就创建SERVLET普通Web类型。
2、使用类加载器加载ApplicationContextInitializer和ApplicationListener
都会使用统一的加载方法getSpringFactoriesInstances进行处理:
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;
}
1)、获取类加载器(默认不会传入)
这里可以思考,同一个class文件,不同的类加载器加载完成后会得到两个类。所以每有特殊情况的话最好不要传入自己的类加载器,只是Spring Boot在这里做了扩展点。
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} // 省略部分代码
return cl;
}
比较情况的看到,或当前线程的类加载器(AppClassLoader类型),保证整个系统中使用同一个类加载器。
2)、通过类加载器和名称获取类全限定名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} // 省略部分代码
}
首先在"META-INF/spring.factories"中获取对于的配置信息,并且按照类加载器进行缓存。缓存对象cache的结构为:Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
3)、根据类加载器、环境参数等信息反射生成类
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;
}
直接通过反射创建对象。
4)、排序(按照@Order等)
所有使用该公共方法获取的类型都可以添加@Order或@Priority或Ordered,最后都会使用java.util.Comparator的子类AnnotationAwareOrderComparator进行排序处理。
二、SpringApplication的run方法(主要流程节点)
public ConfigurableApplicationContext run(String... args) {
// 使用Spring自己的定时器工具
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置Headless
configureHeadlessProperty();
// 获取SpringApplicationRunListener类型
SpringApplicationRunListeners listeners = getRunListeners(args);
// SpringApplicationRunListener生命周期的 starting
listeners.starting();
try {
// 将main方法的args参数进行封装,为后续的CommandLineRunner回调做准备
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印Banner图,就使用默认的SpringBootBanner即可
Banner printedBanner = printBanner(environment);
// 根据SpringApplication构造器出事化的WebApplicationType类型,反射创建ApplicationContext对象
context = createApplicationContext();
// 获取SpringBootExceptionReporter类型
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// Spring的ApplicationContext的refresh前的准备工作(ApplicationContextInitializer完成初始回调,
// SpringApplicationRunListener生命周期的 contextLoaded)
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// Spring的ApplicationContext的refresh,ServletWebServerApplicationContext类型则会在onRefresh阶段执行
// createWebServer完成内嵌Servlet容器的创建
refreshContext(context);
// refresh完成后的动作,默认为空留给子类实现
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
// 如果需要打印日志,则将StopWatch中的启动信息进行打印
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// SpringApplicationRunListener生命周期的 started
listeners.started(context);
// 完成ApplicationRunner和CommandLineRunner的回调(主要用于与args相关的回调)
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 异常处理
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// SpringApplicationRunListener生命周期的running
listeners.running(context);
}
catch (Throwable ex) {
// 异常处理
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
之前说过AbstractApplicationContext的refresh分为模板方法,那当前的run方法也为模板方法,定义了执行流程。那么,当前主要关注的流程节点有:
1)、SpringApplicationRunListener对应的生命周期,一直贯穿着整个的run方法
2)、prepareEnvironment(准备环境)
初始化ConfigurableEnvironment类型,并且赋值给ApplicationContext。可以参见:SpringIoc源码(六)- ApplicationContext(二)- refresh(obtainFreshBeanFactory和StandardEnvironment)
3)、printBanner(打印Banner图)
4)、createApplicationContext(反射创建ApplicationContext对象)
5)、SpringBootExceptionReporter(异常处理器的获取)
为了以后执行异常时,调用SpringBootExceptionReporter列表的回调方法。
6)、prepareContext(ApplicationContext的refresh前的准备工作)
ApplicationContext的refresh前的准备工作(ApplicationContextInitializer完成初始回调,SpringApplicationRunListener生命周期的 contextLoaded)
7)、refreshContext(ApplicationContext的refresh执行)
ApplicationContext的refresh,ServletWebServerApplicationContext类型则会在onRefresh阶段执行。createWebServer完成内嵌Servlet服务器(如:Tomcat、Jetty)的创建。
8)、afterRefresh(模板空方法)
refresh完成后的动作,默认为空留给子类实现
9)、如果需要打印日志,则将StopWatch中的启动信息进行打印
10)、callRunners(main方法args相关回调)
完成ApplicationRunner和CommandLineRunner的回调(主要用于与args相关的回调)
11)、handleRunFailure(中间有异常出现,异常逻辑回调)
详细主要节点,后续进行分析