题语:学习方法之多写:模仿、比较、超越
作者:A哥(YourBatman)
wx号:fsx641385712(备注“Java群”字样)
公众号:BAT的乌托邦(ID:BAT-utopia)
代码下载地址:https://github.com/f641385712/spring-cloud-learning
目录
- 前言
- 为何我决定写Spring Cloud专栏?
- 正文
- 版本约定
- 关于Spring Cloud Context
- 本jar的spring.factories文件解释
- Spring Cloud专属配置类
- Spring Boot容器 vs Spring Cloud容器
- 总结
- 关注我
前言
各位小伙伴大家好,我是A哥。了解我文章的小伙伴应该知道,到目前为止A哥还几乎没有写过Spring Boot/Spring Cloud
的文章,虽然写了不少关于Spring Framework
的内容,但仍旧被不少小伙伴认为与时代脱节。都什么年代了,谁还会直接使用Spring Framework呢?是的没毛病,那么至于为何我一直坚持Spring Framework
才是最重要的呢,请看下图:
你对Spring Framework的了解程度决定了你对Spring Boot的了解程度,你对Spring Boot的了解程度决定了你对Spring Cloud的了解程度。
为何我决定写Spring Cloud专栏?
A哥一直迟迟不去写Spring Cloud系列,主要是“市面上”的Spring Boot/Spring Cloud相关文章实在太多了,感觉这汤饭被炒得太多次了,被玩坏了的赶脚,所以是有畏惧的。经过一段时间的思考,最终A哥还是决定要去“冒险”一波,主要有如下两方面原因促成:
- 通过小伙伴们的反馈,对Spring Cloud体系的学习确实有不小的诉求。这很容易理解:谁让它这么火呢~
- “市面上”的SB/SC系列教程,大都偏使用层面,站在使用者的角度去分析。读完了可能会Run了,可成为一个合格的API调用工程师,但背后的缘由、对于如何扩展仍举步维艰
还记得我刚入门的时我“师父”对我说过一段话,结合现在语境翻译为:如果你对SB/SC不甚了解,并且只是抱着一种just run
的态度去对待,那你的工作会很累且难有积累和提升。不知道为何能run和为何不能run,做起事来心里必然会有所顾虑。Spring Boot/Spring Cloud对初学者非常的友好 ,并且兼具了极大的灵活性,因此不管是对于新手还是老手它都具有很大的学习以及可扩展、可定制空间。
熟悉A哥的小伙伴都知道,我虽然骚气,但并不会擅长写抓人眼球文章,反而更愿意沉下心去写些更为实用的文章,不求访问量,只求产生共鸣。那么为何大多数人都愿意写水文(记录自己生活、唠嗑等文),而不愿意写技术文呢?主要有如下两大原因:
- 技术太过于枯燥,很少人愿意静心阅读
- 技术文一般很难单独成篇,它对上下文(特别是上文)是有依赖的,这也是为何很多人读不下去的原因
- 业内共识:水文写一篇仅需1到2小时,而一篇还不错的技术文往往要花1-2天甚至更多的时间精力
技术文的阅读量一般远远不如“同级别”的水文。有点费力不讨好之嫌。利益驱使,因此坚持这么做的是比较少的,我也不知自己还能坚持几天~
在大环境非常不乐观的当下,唯有技术过硬是你的立身之本,大潮退去才知道谁在裸泳,这对真正的技术人或许是机遇勒。那么来吧,跟着A哥一起的修行之路从这启程,期待本系列做得有血、有肉、有内容、有深度…
正文
何为Spring Cloud?摘抄自官网:Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致样板式样,并且使用Spring Cloud开发人员可以快速站起来实现这些样板的服务和应用程序。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心以及Cloud Foundry等托管平台。
A哥看到很多SC系列开门见山是教你如何去搭建应用,把SC跑起来。但作为只写“高逼格”文章的我自然不会从哪聊起喽,也费你时间不是。本系列会认为你已经对Spring Boot有一定的了解,并且基本能够正常使用SC了,这样效果最佳。
关于Spring Boot我并无打算写它,但我写了一篇“总结篇”,请移步此处参阅:不懂SpringApplication生命周期事件体系?那就等于不会Spring Boot嘛
本文单刀直入,上来就是源码分析以及对应实例讲解,嘎嘎就是干,一把嗦。所以我们学习Spring Cloud那必然先来到它的核心中的核心:Spring Cloud Context
版本约定
不约定版本的专题讲解是不负责任的,特别对于这么“浪”的SC来说尤甚(毕竟不向下兼容在它身上太常见了)。版本上为了和A哥的其它专题系列保持一致方便后续串讲,本文对相关库的版本做出如下约定:
Spring Boot
版本:2.2.2.RELEASE
Spring Cloud
版本:Hoxton.SR1
- Spring Boot版本:2.2.2.RELEASE
- spring-cloud-starter-xxx版本:2.2.1.RELEASE
说明:你阅读到本文时Spring Cloud版本可能已经到了
Hoxton.SR4
,Spring Boot版本也可能已经到了2.2.6.RELEASE
。但是像这种专栏式的学习,你完全可忽略小版本之间的差异,姑且认为他们是一样的即可
工程pom文件的大致内容如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
...
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
关于Spring Cloud Context
也许你从没听过SC的Context上下文概念,或者从没听过spring-cloud-context
这个工程,这都是非常正常的,毕竟实际Just Run条件下确实无需知晓,对初学者非常友好嘛,这是必须做到的。但你应该在pom里见过or导入过spring-cloud-starter
这个jar:
spring-cloud-context
:SC的context上下文、配置、生命周期管理、健康检查等等spring-cloud-commons
:大名鼎鼎的SC的commons抽象。含有如:服务注册/发现、熔断器、负载均衡等公共抽象,面向该抽象编程可以无需关心底层实现,带来一致的编程感受,这是Spring家族最擅长干的事
很明显,本系列将把它拆散开来逐个解释其作用,目的是深入理解,从本质上感受SC的设计以及它的抽象方式和实现方式。那么首先就来到spring-cloud-context
这个工程喽。
GAV如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
说明:实际生产中,此jar你一般不会单独导入,而是伴随着Spring Cloud starter一起带入的。但“讲课”就得基于最小化原则进行讲解嘛,因此我需要最小化的依赖,我的工程里只导入此jar
本jar的spring.factories文件解释
这个jar很“单纯”,并不拖泥带水,它里面存在一个spring.factories
文件,内容如下:
到这里了可别问
spring.factories
是什么?有什么作用之类的话了哈。它是Spring SPI机制(并非Spring Boot提供的功能哟)的实现,具体参考这个类SpringFactoriesLoader
# AutoConfiguration 自动配置类(一般情况下只有SB容器去执行它,但是不是确定的)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners 监听器们
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ -> 监听ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ -> 监听ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.context.restart.RestartListener -> 监听ApplicationPreparedEvent/ContextRefreshedEvent事件
# Bootstrap components 由Spring Cloud容器负责加载的配置类们
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
此配置文件内容一共分为三个部分,会涉及到执行顺序的问题:
再次有必要强调一次,建议你已经掌握了此文的知识效果更佳:不懂SpringApplication生命周期事件体系?那就等于不会Spring Boot嘛
- 自动配置类。有的小伙伴认为自动配置类只能是Spring Boot容器才去执行它,其实这是不对的。确切的说:只要容器内开启了自动配置(有Bean标注了
@EnableAutoConfiguration
注解,实际是向容器导入了AutoConfigurationImportSelector
)就都会执行执行spring.factories
里的自动配置类- Spring Boot自然不用说,
@SpringBootApplication
里就默认开启了自动配置 - Spring Cloud里,虽然也使用
SpringApplication
去启动容器,但是因为SC容器并没有开启自动配置,所以它不会执行 - 强调说明:自动配置的执行时机是
refresh()
容器启动时(执行AutoConfigurationImportSelector#selectImports)。另外,如果你的容器并不需要开启自动配置(就像SC一样),请不要开启,挺耗时的(前提是你对SB、SC有足够了解,不然容易出bug)。或者你也可以使用ImportAutoConfigurationImportSelector
来手动接管
- Spring Boot自然不用说,
- 监听器。如上标注,这几个监听器都是监听的SpringApplication的生命周期事件,因此它们在SB的容器启动时的不同生命周期里会按顺序执行,特别是最重要的
BootstrapApplicationListener
它负责Spring Cloud容器的启动工作- 监听器是由SpringApplication应用去加载的,而SC也是由SpringApplication去启动的,所以SC也会让这些监听器挨个执行哦,若有必要,请注意“防范”
- Spring Cloud的启动/引导配置。这些配置是SC专属,详见下面的说明
Spring Cloud专属配置类
也叫Bootstrap
引导容器的专属配置类。通过上面3步的第一步解释可知:Spring Cloud它并不会去执行/加载你配置的EnableAutoConfiguration
自动配置类,那么如果SC它在引导期间需要属于自己的配置怎么办呢???
这就到了SC它专为自己开发出来的一套配置机制,姑且把它叫做Spring Cloud专属配置类。不同于Spring Boot自动配置使用的是org.springframework.boot.autoconfigure.EnableAutoConfiguration
,它作为SC专属的,自然key也就不能一样喽,使用的是:org.springframework.cloud.bootstrap.BootstrapConfiguration
(@BootstrapConfiguration
注解的全类名)。通过这个key配置的配置类们(可以是普通配置类,也可以是自动配置类),就是只能被SC容器在启动时加载的专属配置类,是SC父容器专属。
SB的自动配置类使用
AutoConfigurationImportSelector
负载最后加载,此处SC的专属配置类使用的是BootstrapImportSelector
负责加载,它俩的共同点是:都是一个DeferredImportSelector
,所以执行时机是最晚的
通过以上的了解,我们可以知道,SC的专属配置类有如下几个特点:
- 该配置只能被Spring Cloud容器/Bootstrap引导上下文读取/加载
- 该配置的加载时机最早:优先于SB的普通配置、自动配置之前加载,所以它是具有最高优先级的配置类
- 该配置存在于父容器(SC容器)内,而非SB容器内
- 当然喽,SB容器也可以访问
Spring Boot容器 vs Spring Cloud容器
我们知道,Spring Cloud容器是Spring Boot容器的父容器。为了让你更直观的看到父子容器内的Bean情况(个数 + 详情),了解其区别,此处A哥写个最简案例比较一波:
public static void main(String[] args) {
ConfigurableApplicationContext bootContext = SpringApplication.run(Application.class, args);
System.out.println("boot容器类型" + bootContext.getClass());
System.out.println("boot容器Bean定义总数:" + bootContext.getBeanFactory().getBeanDefinitionCount());
System.out.println("boot容器Bean实例总数:" + bootContext.getBeanFactory().getSingletonCount());
ConfigurableApplicationContext cloudContext = (ConfigurableApplicationContext) bootContext.getParent();
System.out.println("cloud容器类型" + cloudContext.getClass());
System.out.println("cloud容器Bean定义总数:" + cloudContext.getBeanFactory().getBeanDefinitionCount());
System.out.println("cloud容器Bean实例总数:" + cloudContext.getBeanFactory().getSingletonCount());
}
控制台输出:
boot容器类型class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
boot容器Bean定义总数:272
boot容器Bean实例总数:283
cloud容器类型class org.springframework.context.annotation.AnnotationConfigApplicationContext
cloud容器Bean定义总数:22
cloud容器Bean实例总数:34
SC容器(父容器)里的“内容”是远远少于SB容器(子容器的)。由于父容器里内容较少,可以详细看看。A哥这里帮你截图让你更直观的感受一把喽:
context.getBeanFactory().getBeanDefinitionNames()
(共22个):
context.getBeanFactory().getSingletonNames()
(共34个):
看完了这个结果,相信你有个感官的认识了。那么A哥给小伙伴们提3个小问题哈,你可以自行思考:
- 为何一个是34,一个是22,这两个值为毛不等呢?差异在哪儿呢?
- SB容器为毛只是单单启动完,就有这么多Bean了呢?为何它如此重呢?
- SB应用有几个context上下文?SC应用呢?
总结
关于Spring Cloud系列的第一篇内容就写到这喽。本文先解释了为何我要写这个系列,以及简单的介绍了下Spring Cloud Context
工程,通过本文你是能够大概清楚A哥后面将如何展开的,比如下文将是本系列的主菜之一:BootstrapApplicationListener
监听器负责Spring Cloud容器的引导/启动/初始化,敬请关注。
关注我
本号所有“享学xxx”系列文章/视频,和什么“享学课堂”、“享学科技”无任何关系。只因笔者名字和其重名,仅此而已
- 原创不易,码字更不易,请勿白嫖,你的三连是对A哥的最大支持
- 关注我吧:一个前25年还不会写Hallo World的半残程序猿,中文名:A哥,英文名:YourBatman
- 公众号:BAT的乌托邦(ID:BAT-utopia)
- CSDN:BAT的乌托邦
- 掘金:BAT的乌托邦
- 腾讯云+社区:BAT的乌托邦
- 知识星球:BAT的乌托邦
- 扫码/加我wx:
fsx641385712
,邀你加入[Java高工、架构师]系列纯纯纯技术群 - 主页地址:【A哥】的个人空间
- Netflix OSS套件一站式学习驿站
- Netflix OSS套件一站式学习驿站
- [享学Ribbon] 一、源生Ribbon介绍 — 客户端负载均衡器
- [享学Ribbon] 二、Ribbon核心API源码解析:ribbon-core(一)IClient请求客户端
- [享学Ribbon] 三、Ribbon核心API源码解析:ribbon-core(二)IClientConfig配置详解
- [享学Ribbon] 四、Ribbon核心API源码解析:ribbon-core(三)RetryHandler重试处理器
- [享学Ribbon] 五、Ribbon核心API源码解析:ribbon-core(四)ClientException及常用工具
- [享学Ribbon] 六、Ribbon的LoadBalancer五大组件之:IPing心跳检测
- [享学Ribbon] 七、Ribbon的LoadBalancer五大组件之:ServerList服务列表
- [享学Ribbon] 八、netflix-statistics详解,手把手教你写个超简版监控系统
- [享学Ribbon] 九、Ribbon服务器状态:ServerStats及其断路器原理
- [享学Ribbon] 十、Ribbon负载均衡策略服务器状态总控:LoadBalancerStats
- [享学Ribbon] 十一、Ribbon多区域选择:ZoneAvoidanceRule.getAvailableZones()获取可用区
- [享学Ribbon] 十二、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
- [享学Ribbon] 十三、Ribbon的LoadBalancer五大组件之:ServerListFilter服务列表过滤器
- [享学Ribbon] 十四、Ribbon的LoadBalancer五大组件之:ServerListUpdater服务列表更新器
- [享学Ribbon] 十五、Ribbon的LoadBalancer五大组件之:IRule(一)轮询和加权轮询
- [享学Ribbon] 十六、Ribbon的LoadBalancer五大组件之:IRule(二)应用于大规模集群的可配置规则
- [享学Ribbon] 十七、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结
- [享学Ribbon] 十八、Ribbon启动连接操作:IPrimeConnection检测Server是否能够提供服务
- [享学Ribbon] 十九、Ribbon负载均衡器执行上下文:LoadBalancerContext
- [享学Ribbon] 二十、Ribbon负载均衡器ILoadBalancer(一):BaseLoadBalancer
- [享学Ribbon] 二十一、Ribbon负载均衡器ILoadBalancer(二):ZoneAwareLoadBalancer具备区域意识、动态服务列表的负载均衡器
- [享学Ribbon] 二十二、Ribbon负载均衡命令:LoadBalancerCommand(一)基础类打点
- [享学Ribbon] 二十三、Ribbon负载均衡命令:LoadBalancerCommand(二)执行目标请求
- [享学Ribbon] 二十四、Ribbon具有负载均衡能力的客户端:AbstractLoadBalancerAwareClient
- [享学Ribbon] 二十五、Ribbon和Eureka的整合(一):ribbon-eureka工程详解
- [享学Ribbon] 二十六、Ribbon和Eureka的整合(二):DiscoveryEnabledNIWSServerList从Eureka Server获取服务列表