什么样的经历,才能领悟成为架构师? >>>
什么样的经历,才能领悟成为架构师? >>>
本文主要分析 SpringBoot 的启动过程。
SpringBoot的版本为:2.1.0 release,最新版本。
一.时序图
还是老套路,先把分析过程的时序图摆出来:时序图-SpringBoot2.10启动分析
二.源码分析
首先从我们的一个SpringBoot Demo开始,这里使用 SPRING INITIALIZR 网站生成的starter开始的:
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
// 分析的入口,从 run 方法开始
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
经过SpringApplication多个重载的构造方法,最后到达:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 从 run() 传入的 resourceLoader 此处为 null
this.resourceLoader = resourceLoader;
// 使用断言判断 resourceLoader 不为空
Assert.notNull(primarySources, "PrimarySources must not be null");
// 把 primarySources 数组转为List,最后放入 primarySources 的一个LinkedHashSet中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断应用的类型:REACTIVE NONE SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 实现 SpringBoot 自动装配的基础,此处Spring自己实现的SPI(从META-INF/spring.factories加载class)
// 加载并实例化以 ApplicationContextInitializer 为key的类
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 加载并实例化以 ApplicationListener 为key的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 获取程序当前运行堆栈,看是运行的是哪个类的 main 方法,保存到上下文中
this.mainApplicationClass = deduceMainApplicationClass();
}
看一眼,WebApplicationType#deduceFromClasspath ,deduce意为推断,即根据classpath下的内容推断出应用的类型。实现是通过ClassUtils#isPresent来尝试加载代表不同应用类型特征的Class文件:
static WebApplicationType deduceFromClasspath() {// 判断应用的类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)// 加载到DispatcherHandler
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)// mvc的DispatcherServlet
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {// jersey的ServletContainer
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {// 遍历数组:Servlet和ConfigurableWebApplicationContext
if (!ClassUtils.isPresent(className, null)) {// 没有加载到Servlet相关的class
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
SpringApplication#getSpringFactoriesInstances,从类路径下 META-INF/spring.factories 下加载 SpringFactory 实例,类似的操作在 Dubbo SPI中也有:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// 此处调用了SpringFactoriesLoader的loadFactoryNames()
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// Use names and ensure unique to protect against duplicates
// 实例化获取到的类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);// 排序
return instances;// 返回实例化好的对象
}
SpringFactoriesLoader#loadFactoryNames,加载工厂名字:
public static List<String> www.yongshiyule178.com loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName =www.mcyllpt.com/ factoryClass.getName(www.leyouzaixian2.com);
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
继续捉迷藏,到了 SpringFactoriesLoader#loadSpringFactories:下面的内容就是找到所有classpath下的 spring.factories 文件,读取里面的内容,放到缓存中,此处和Dubbo SPI中ExtensionLoader#loadDirectory几乎是一模一样,可以参考我写过的 Dubbo源码 里面的注释。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 从缓存中获取Map,key为classLoader
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 加载资源的urls,被加载的资源为 "META-INF/spring.factories"
//先从Resources中加载,没有加载到再从SystemResources中加载
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {// 遍历加载到的 spring.factories 文件
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 读取文件到内存为Properties对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<www.michenggw.com/?, ?> entry :www.yigouyule2.cn properties.entrySet()) {
// Entry的key作为工程Class的名字
String factoryClassName = ((String) www.gcyl152.com/ entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
// 如果有多个value,都放在Map中,注意此处为 MultiValueMap ,不是普通的Map,其实现内容的value对应一个LinkedList
result.add(factoryClassName, factoryName.trim(www.gcyl159.com));
}
}
}
// 最后把读取配置的结果都放入缓存中,cache对象为一个ConcurrentReferenceHashMap
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
我们也来看一下上面读取的文件 spring.factories 的内容,大概长这个样子:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
......
是时候跳出来了,回到主线,返回实例化对象后,到了 SpringApplication#deduceMainApplicationClass,获取程序当前运行堆栈,看现在运行的是哪个类的 main 方法,然后保存到上下文:
private Class<?> deduceMainApplicationClass() {
try {
// 拿到运行时的堆栈信息
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 如果发现哪个堆栈元素里面有运行了main方法,则返回该类
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
至此,SpringApplication的构造函数的分析完成,后面我们继续分析SpringApplication的run()方法中做了哪些操作。
什么样的经历,才能领悟成为架构师? >>>的更多相关文章
- 一个4年工作经验的java程序员的困惑,怎样才能能为一个架构师,请教大神
一个4年工作经验的java程序员的困惑,怎样才能能为一个架构师 LZ本人想往架构师发展, 业余时间也会看一些书籍, 但是感觉没有头绪, 有些书看了,也没有地方实践 我做了4年的java开发, 在一个公 ...
- JAVA兼职架构师
在一些小企业或者公司人力不足的时候,经常会出现一个人干多个人的活.开发可能会干架构.测试.运维,一些小项目可能需要一个人完成.我把这些角色合并在一起称之为兼职架构师. 我用我的经历来说说兼职架构师的需 ...
- 向架构师进军--->系统架构设计基础知识
如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 在讲解系统架构设计之前,有必要补充一下架构相关的概念,因此本博文主要讲述架构.架构师 ...
- 谈谈.NET架构师面试及如何设计面试题
上星期:应老东家的要求,帮其面试.NET架构师. 于是:老东家进行了一星期的简历收集: 终于:在一堆简历里,精挑细选了四个: 约了:周末上午下午各两个. 面试者年龄:在30-35岁左右,差不多10年. ...
- 阿里Java架构师分享自己的成长经历,教你如何快速成长为架构师
架构师是公司的“金领”,很少需要考虑生存的问题,从而有更多的精力思考关键技术,形成“强者愈强”的良性循环.当然,冰冻三尺非一日之寒,成为一名合格的架构师是一个漫长的积累过程.对于大部分的软件开发人员来 ...
- 周爱民:真正的架构师是没有title的(图灵访谈)
周爱民,现任豌豆荚架构师,国内软件开发界资深软件工程师.从1996年起开始涉足商业软件开发,历任部门经理.区域总经理.高级软件工程师.平台架构师等职,有18年的软件开发与架构.项目管理及团队建设经验, ...
- 架构师修炼 III - 掌握设计原则
关于软件的设计原则有很多,对于设计原则的掌握.理解.实践及升华是架构师的一项极为之必要的修炼. 记得在12年前第一次阅读<敏捷开发>时,五大基本设计原则就深深地植入到我的脑海中一直影响至今 ...
- 架构师素养及从小菜进阶架构(CTO)的书籍【转】
CTO要了解无线技术/搜索/大数据/数据库等. -- 通常定义架构有几个层次,这包括业务架构.产品架构.应用架构和技术架构: 1.业务架构:描述一个企业围绕一个行业做了哪些业务,例如支付行业的收单.退 ...
- 【转载】WEB架构师成长之路
本人也是coding很多年,虽然很失败,但也总算有点失败的心得,不过我在中国,大多数程序员都是像我一样,在一直走着弯路,如果想成为一个架构师,就必须走正确的路,否则离目标越来越远,正在辛苦工作的程序员 ...
随机推荐
- [已读]编写可维护的javascript
13年4月份出版,作者是大名鼎鼎的Zakas,他的另两本书<javascript高级程序设计>与<高性能javascript>你一定听过或者读过. 这本书重点讲了编码风格和编码 ...
- Aria's Loops
https://www.hackerrank.com/contests/101hack41/challenges/arias-loops 可以看我以前的笔记,http://www.cnblogs.co ...
- 分布式数据存储 之 Redis(一) —— 初识Redis
分布式数据存储 之 Redis(一) -- 初识Redis 为什么要学习并运用Redis?Redis有什么好处?我们步入Redis的海洋,初识Redis. 一.Redis是什么 Redis 是一个 ...
- ubuntu安装mysql多实例
想要尝试mysql的读写分离,在云上安装完mysql之后突然想到一个问题:我本机是没有公网IP的. 开始尝试在唯一一台云服务器上安装多个mysql实例. 主要步骤: 1.新建MySQL目录 (1):新 ...
- disconf 分布式配置
摘要 为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案(简称为disconf[4],下同).首先,实现了同构系统的配置发布统一化,提供了配置服务se ...
- virtualbox没有64位选项
今天安装的virtualbox想安装一下sql server 测试一下 在安装系统的时候发现没有64位系统的选项,在网上找了一下 发现是 在BIOS里面有一个选项没有开启, 是 Intel virt ...
- whatis命令
whatis——于查询一个命令执行什么功能 示例1: # whatis ls 显示ls命令的功能,和执行man命令时NAME信息差不多
- Redhat5 安装序列号及版本说明
为了保证安装的组件和订阅相匹配,红帽企业 Linux 5 需要输入一个安装号.它被用来配置安装程序来提供正确的软件包.安装号码包含在你的订阅里. 如果您没有输入安装号码,只有核心服务器或 Deskto ...
- WEB前端研发工程师编程能力成长之路(2)(转)
WEB前端研发工程师编程能力成长之路(2) 四.[入微] 最强解决方案.你能够走在需求的前面,将当前需求里有的.没有直接提出来的.现在暂时没有但将来可能有的等等,及前端编程潜规则等各个方方面面都综 ...
- HashMap Hashtable TreeMap LinkedHashMap 分析
首先对hash的了解:就是关键字,和数据建立关系的映射. hash常用算法:假设我们中的字符有相应的内部编码,当然在实际过程中,我们不可能将所有的编码当做hash值. 平方取中法,将所得的内部编码平方 ...