在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数。

先把这段run函数的代码贴出来:

	/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

我们先来分析其中的第一个关键代码:SpringApplicationRunListeners listeners = getRunListeners(args);

这行代码是获取监听器,我们先跳转到getRunListeners中看一下:

	private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}

在这段代码中,我们看到获取监听器,是new出来了一个SpringApplicationRunListeners实例并返回。

再次跳转到SpringApplicationRunListeners的构造函数中,看到一下发生了什么:

    SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList(listeners);
}

在这个构造函数里,我们看到其只是把接收到的listeners参数,保存到实例变量里,没有过多的操作。

所以,重点是在listeners参数这里,而listeners是通过getSpringFactoriesInstances创建出来的,来看一下getSpringFactoriesInstances函数做了什么事情吧:

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 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;
}

在这里我们看到,首先创建了一个classloader,然后用SpringFactoriesLoader.loadFactoryNames(type, classLoader),加载了SpringFactoriesLoader列表。我们来看一下loadFactoryNames里面的代码:

    public static List<String> loadFactoryNames(Class<?> factoryClass,
@Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,
Collections.emptyList());
} private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = (MultiValueMap)cache.get(classLoader);
if(result != null) {
return result;
} else {
try {
Enumeration ex = classLoader != null?
classLoader.getResources("META-INF/spring.factories")
:ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result1 = new LinkedMultiValueMap(); while(ex.hasMoreElements()) {
URL url = (URL)ex.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) {
Entry entry = (Entry)var6.next();
List factoryClassNames = Arrays.asList(StringUtils
.commaDelimitedListToStringArray((String)entry.getValue()));
result1.addAll((String)entry.getKey(), factoryClassNames);
}
} cache.put(classLoader, result1);
return result1;
} catch (IOException var9) {
throw new IllegalArgumentException(
"Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}

通过这里我们看到了其首先加载了META-INF/spring.factories这个配置文件下的所有资源,并放入缓存,然后再获取了org.springframework.context.ApplicationListener定义的资源列表。

小发现:在这里我们发现spring boot自动装配文件的位置。

获取到META-INF/spring.factories这个配置文件下的资源名称列表以后,通过createSpringFactoriesInstances函数创建了SpringFactories的实例。

    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
ArrayList instances = new ArrayList(names.size());
Iterator var7 = names.iterator(); while(var7.hasNext()) {
String name = (String)var7.next(); try {
Class ex = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, ex);
Constructor constructor = ex.getDeclaredConstructor(parameterTypes);
Object instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name,
var12);
}
} return instances;
}

通过上面的这些代码流转,我们大概搞清楚了listeners是怎么创建出来的。

然后调用了listeners的starting方法。我们先大概地看一下EventPublishingRunListener里面的starting的实现:

	public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application,
this.args));
}

关键代码在这里:

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator(); while(var4.hasNext()) {
ApplicationListener listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if(executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
} } protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if(errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
} }

在上面代码中我们看到,starting就是拿到META-INF/spring.factories中定义的资源的实例以后,然后再创建一个线程去启动起来。

通过上面的这些代码我们知道了spring boot会获取META-INF/spring.factories中的资源,并创建这些资源的实例(listeners监听器),然后为每一个监听器创建一个线程启动起来。

篇幅有限, 今天就写到这里吧。有希望一起学习spring boot 2.0源码的同学可以关注我,跟我一起分析spring boot 2.0 源码的实现方式。

spring boot 2.0 源码分析(二)的更多相关文章

  1. spring boot 2.0 源码分析(四)

    在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...

  2. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  3. spring boot 2.0 源码分析(三)

    通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...

  4. spring boot 2.0 源码分析(五)

    在上一篇文章中我们详细分析了spring boot是如何准备上下文环境的,今天我们来看一下run函数剩余的内容.还是先把run函数贴出来: /** * Run the Spring applicati ...

  5. Spring Boot 自动配置 源码分析

    Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...

  6. 前端MVC框架Backbone 1.1.0源码分析(二) - 模型

    模型是什么? Models are the heart of any JavaScript application, containing the interactive data as well a ...

  7. Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二)

    Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二) 题记:  前文<Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)>中提 ...

  8. Solr4.8.0源码分析(19)之缓存机制(二)

    Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...

  9. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

随机推荐

  1. DevExpress 使用条形码二维码控件打印

    参考文章: https://www.cnblogs.com/wuhuacong/p/6112976.html 转载请注明出处:撰写人:伍华聪 其实主要是二维码的实现,在使用条形码控件时,又一个属性Sy ...

  2. 题解 P1550 【[USACO08OCT]打井Watering Hole】

    题面(翻译有点问题,最后一句话) 农民John 决定将水引入到他的n(1<=n<=300)个牧场.他准备通过挖若 干井,并在各块田中修筑水道来连通各块田地以供水.在第i 号田中挖一口井需要 ...

  3. 团队作业——Alpha冲刺 9/12

    团队作业--Alpha冲刺 冲刺任务安排 杨光海天 今日任务:修复编辑界面与弹窗界面合并中出现的BUG 明日任务:希望完成编辑界面所有接口交互的功能 郭剑南 今日任务:优化图像预处理所有功能的函数代码 ...

  4. facebook api & oauth protocal

    http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-10.5 http://stackoverflow.com/questions/14 ...

  5. TML 打印预览问题,怎么设置有些内容不出现在打印预览页面上。怎么控制,有下代码 看得不是很懂 求解释

    HTML <style> 标签的 media 属性 HTML <style> 标签 实例 针对两种不同媒介类型的两种不同的样式(计算机屏幕和打印): <html> ...

  6. zabbix 客户端添加自定义参数

    0.zabbix客户端配置文件 server和serveractive都需要配置 1.配置自定义参数脚本文件 zabbix_agent.conf中UserParameter字段,可以配置多行 User ...

  7. leetcode16—3 Sum Closet

    Given an array nums of n integers and an integer target, find three integers in nums such that the s ...

  8. .NET批量操作窗口样式

    1. 背景 我们在开发过程中,可能会遇到需要批量控制程序中窗体的大小或其它一些操作, 这些窗体有可能是属于程序本身的,也许是其它程序的窗口.本文就是基于此的一篇关于如何批量操作窗口样式的,我们主要是通 ...

  9. HDU1166

    https://vjudge.net/contest/66989#problem/A C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直 ...

  10. WebView之javascript与android交互基础加强

    一.什么是js与android交互? 通俗一点就是使用js代码调用java代码,或者使用java代码调用js代码. 二.为什么要使用js与java代码交互? 1.可以做一些js网页做本身处理不了的事情 ...