代码下载地址:https://github.com/f641385712/netflix-learning
目录
-
- 前言
- 正文
-
- Spring Boot整合Spring Cloud
- 概念辨析
-
- 应用 vs 上下文 vs 容器
-
- 应用Application
- 上下文Context
- 容器
- 父容器 vs 子容器
- Environment环境
-
- 属性源
-
- 我该如何记忆Spring Boot属性优先级?
- 附:Spring Boot所有版本外部化配置一览
- 2020-11-22新增
- application.yaml vs bootstrap.yaml
- 总结
- 关注A哥
前言
各位小伙伴大家好,我是A哥。通过上文 为何我决定写Spring Cloud专栏 一方面了解了A哥的“创作背景”;另一方面,当然也是最主要的便是我们已经初步了解了Spring Cloud Context
工程,并且对它的spring.factories
文件做了介绍,从中是能窥探出SC的核心组成“元素”的。
天若有情天亦老,人间正道是沧桑。接下来会进入到“枯燥乏味”的修行当中,坚持下来的都是英雄。准备深入去了解Spring Cloud,BootstrapApplicationListener
无疑是它的第一入口,然而在这之前,我们依旧需要扫除一些“障碍”。
正文
关于Spring Boot整合,请看我在度娘上的搜索推荐结果:
Spring Boot整合Spring Cloud
众所周知,Spring Cloud
是构建在一个Spring Boot应用的基础之上的,广义上说可以认为是SB应用把Spring Cloud
整合进来了。谈到技术整合相信各位小伙伴轻车熟路:Redis整合、ElasticSearch整合、MyBatis整合…Spring Cloud的整合方式有点不太一样。SC它并非作为一个组件那么直接的加入到Spring Boot的ApplicationContext
上下文/容器里即可,而是拥有自己一套独立的ApplicationContext上下文,然后通过上下文之间的关系(父子关系)完成和Spring Boot的“整合”。
如果说Mybatis、Redis这种整合方式是单个、直接方式;那么Spirng Cloud这种便是批量的、间接的方式,而正因为Spring Cloud有自己的Context上下文,所以它自己也就拥有整合其它组件的能力,这是非常重要的。
其实把Eureka、Hystrix、Ribbon等和Spring Boot直接整合也是可行的,但是这并不符合SB单应用的定位,并且Spring Framework和Spring Boot团队并不擅长解决和网络、云计算相关的技术,因此专门开设Spring Cloud子项目负责“接管”,保证了Spring Boot的纯粹性。
概念辨析
在正式开始Spring Cloud内容学习之前,我觉得有几个概念性的知识点需要先辨析一下,这个工作很有必要。
应用 vs 上下文 vs 容器
作为一个Java coder,这三个概念应该是常绕耳边的。不可否认,这三个概念非常重要,它亦会在本系列文章中贯穿始终,因为它就是用来沟通的“语言”嘛。但是,常常听见并不代表真的理解,特别这种“近义词”,本处就对这几个概念作出辨析。
以个人理解作出的小总结,若有误请留言指正,A哥一直都是个虚心向任何人学习的好孩纸
应用Application
应用是个比较大概念,比如一个QQ、微信我们均可称为是一个应用,而此处我们一般指的是Spring应用。比如我们一个Spring Boot工程就是一个SpringApplication
实例,也就是一个Spring应用,另外它也称为主应用,一个工程主应用只会有一个。
通常来讲,一个应用只有一个上下文,所以应用和上下文这两个概念常常被误以为是对等的,其实非也,他俩并非同一层面的东东。举个例子:以Spring Boot为例,有些监听器注册在SpringApplication
应用上作用于监听应用的变化(如生命周期监听器、初始化器等),它们并没有放进Spring容器内所以对上下文无感;而有些监听器是被放进容器里的,它的作用区域便就是和容器、Bean相关喽
另外,需要知道的是SB和SC的context上下文均是通过SpringApplication
来构建的,但该SpringApplication
实例在构建出Context后一般随即就“消失了”,这也是很多小伙伴误认为应用 == 上下文的原因之一吧;另外,父子容器概念属于context上下文级别非应用级别
上下文Context
同理,我们一般指的是Spring上下文,也就是ApplicationContext
。在SB/SC里,实际使用的是ConfigurableApplicationContext的子类:AnnotationConfigServletWebServerApplicationContext
和AnnotationConfigApplicationContext
(前者由Spring Boot提供,后者由Spring Framework提供)。ApplicationContext是具有层级关系(父子关系)的,ApplicationContext#getParent()
证明了这一点。
ApplicationContext上下文里的内容非常“丰富”,如Environment环境、BeanFactory工厂、协议解析器等等它都有,毕竟具备这些能力才算一个“合格的”上下文嘛。另外针对于在SC环境下的上下文情况需要有如下共识:
- SB和SC各自拥有一个独立的上下文,它们之前互相隔离却又有联系:SC上下文是SB的父上下文
容器
容器这个概念就更抽象了,在Java界也有不少如web容器、servlet容器等等,但是大多数情况下(包括本系列文章)我们一般指的是IoC容器,也就是Spring容器。
说明:IoC容器的实现其实不仅仅只有Spring,还有也比较流行的Google的开源库Guice,也是个非常好用轻量级DI管理库,有一些第三方开源组件如Eureka、Ribbon、Druid等都是基于它构建的,有兴趣可自己玩玩,也可参考我文章有写到~
对于Spring容器,我们有时候也叫它Bean工厂,也即是BeanFactory
嘛。实际上ApplicationContext
继承了BeanFactory
接口,所以也是个容器(高级别容器),因此在概念上:上下文和容器这两个概念一般是可以对等的。
- 槽点:Spring的Bean容器为毛命名为
BeanFactory
而不是BeanContainer
呢?
对于一个应用(如SB应用),ApplicationContext上下文/容器是可以有多个的。上下文之间的“隔离性”是很好的,后面会看到Ribbon、Feign等它们均会有自己的上下文,也就是学SC一样,通过上下文级别间接的完成和Spring Boot应用的整合。
那么问题来了:何时直接整合?何时需要自己创建一个上下文来管理呢?该问题就作为课后作业,留给读者你自行思考哈~
父容器 vs 子容器
父子关系是一种分层思想,被广泛应用于Spring容器/上下文的组织中,如HierarchicalBeanFactory
。ApplicationContext#getParent()
能证明Spring的上下文也是具有层级结构的。
同义词:父上下文、子上下文。但是切记不可说成父应用和子应用,因为一个Spring Boot工程只会有一个应用,而一个应用内可以有N多个上下文,它们之间可以有关,亦可无关,这是合理的。
Environment环境
一般我喜欢叫它Spring的环境抽象,该“实例”代表着上下文的运行环境,主要包含如下三部分内容:
PropertySource
:属性源。key-value属性对抽象。环境内会有多个属性源,使用MutablePropertySources
来组织PropertyResolver
:属性解析器。用于解析相应key的value,比如你熟悉的PropertySourcesPropertyResolver
就是实现Profile
:配置(资料里翻译为剖面、侧面,也是阔仪的)。只有激活的配置profile的组件/配置才会注册到Spring容器,类似于maven中profile
Environment
抽象Spring一共提供了三个实现类:
- 用于非web环境的:
StandardEnvironment
- 用于web环境的:
StandardServletEnvironment
和StandardReactiveWebEnvironment
,他俩均为StandardEnvironment
的子类。- 需要注意的是:后者
StandardReactiveWebEnvironment
由Spring Boot提供,而非Spring Framework源生
- 需要注意的是:后者
这里小带一句:很多同学把属性和配置傻傻分不清楚,其实他俩就是Properties(k-v)和Profile概念上的差异,但口头上经常混用,一般可认为讲的是同一回事,但是你心里必须清楚哈。毕竟和专业人士聊起来的时候,还是需要这些细节掌握的
尴尬爆料:今天猛然发现,之前好多文章里我把Environment单词写错了,写为了Enviroment,特此提醒各位不要“学我”哦
属性源
Spring通过Environment建模Spring应用运行的环境,这其中最为重要的非属性源莫属。一个属性源就是一个PropertySource
,它可以来自任意地方,如常用的有:
systemProperties
-> 来自 system.getProperties()systemEnvironment
-> system.getenv()random
-> 来自一个Random对象,可用于生成随机数(SB提供)applicationConfig: [classpath:/application.yaml]
-> SB主配置,这个太常见了吧- …
属性源的优先级决定了配置的优先级。Spring Boot对其外部化配置优先级有文档说明:Spring Boot外部化配置
我该如何记忆Spring Boot属性优先级?
答案:不要记。精确答案:不要强记,因为“记不住”的。我见过不少(非常多)小伙伴一遇到需要考虑Spring Boot属性优先级问题时,整头就大了,立马拿出自己的小本本,又或者去度娘里查,或许那会还在内心里骂自己:怎么老是记不住呢?甚至质疑自己:是不是真的老了?
我说的此现象,你是否躺枪?授之以鱼不如授之以渔,对于这种“高难度、易出错”的知识点,浮于表面总觉浅,掌握其本质才是永恒。当然我这里不会去详讲Spring Boot这块的知识点,但我还是呕心沥血的整理出一堆截图,告诉你为何你强行记忆是记不住的(如果你是“天才”你就可以跳过本文了):
附:Spring Boot所有版本外部化配置一览
Spring Boot
从它的1.0.x版本到现在的2.2.x版本,对于外部化配置的支持几乎是一直在变化/增加的,这种不确定性也无疑在一定程度上增加了我们记忆的难度。下面我从其官方文档截图出了它所有版本(请忽略小版本号)对应的支持的外部化配置情况:
说明:以下所有截图都来自Spring Boot官方文档的外部化配置
Externalized Configuration
部分,优先级从高到低。
1.0.0.RELEASE(7个):
1.1.0.RELEASE(9个):@PropertySource优先级有调整,增加了JNDI和random属性源支持
1.2.0.RELEASE(9个):
1.3.0.RELEASE(12个):增加了JSON格式配置、支持-{profile}.xxx
格式的配置
1.4.0.RELEASE(15个):增加了@TestPropertySource支持、ServletConfig和ServletContext属性源的支持
1.5.0.RELEASE(17个):增加了对调试工具@Devtools配置的支持、@SpringBootTest#properties的支持
2.0.0.RELEASE(17个):
2.1.0.RELEASE(17个):
2.2.0.RELEASE(17个):从2.2.0版本后,官方文档采用H5来重写了,所以页面风格整体上有所改变(更高级些了有木有)
此处对@PropertySource属性源增加了文字解释:它在refresh()容器之前,该属性源是不会放进Environment里面的,也就是说在容器启动之前请不要使用它里面的k-v,这在日常使用请你务必注意~
从这能看出Spring Boot对外部化配置发展的脉络。即使从1.5.0
版本后,外部化配置趋于稳定,但谁知道以后呢?对吧~况且,17个耶,你真记得住?最为致命的是:如果你的工程接入配置中心来获取配置,又或者你二次开发自定位你配置的位置,它们都还不在这17个行列里,那肿么办?怎么判断属性优先级呢?所以嘛,像这种case,不要强记,不要强记,不要强记。
2020-11-22新增
截至此时,Spirng Boot 2.4.0已经发布,它带来了全新的配置文件加载机制,不具备向下兼容性,因此本处特别的过来加上。
它官网对配置文件顺序描述也发生了变化,如下图:
阅读此部分配置项需要注意:
- 文档中排列顺序不同于以往,这里是倒序(既最后面的是优先级越高的),请查看时注意哦
- Config data(序号是3)被当作一个整体参与排序,它内部维持着自己的顺序,这样更方便理解了嘛
- 总的算下来还是17种方式(14 - 1(序号3) 4(序号3的详细) = 17)
Spring Boot自2.4.0版本后对配置文件的加载方式完全不同于以往,具体原理分析、使用注意事项、迁移指导等,请参见此系列文章的详解:Spring Boot 2.4.0正式发布,全新的配置文件加载机制(不向下兼容)
application.yaml vs bootstrap.yaml
对于它俩的比较理应放在最后,但这里先睹为快。
application.yaml/properties
被Spring Boot容器读取,也常常被我们称为主配置bootstrap.yaml/properties
被Spring Cloud容器读取,也常常被我们称为引导配置
bootstrap.yaml
优先于application.yaml
被加载。虽然前者是给Spring Cloud专用的,但是这个属性源最终也会“合并”到Spring Boot的属性源里面去,并且优先级高于**application.yaml
**,所以Spring Boot里也可以使用。
说明:
application.yaml
里的属性,SC可是完全触碰不到的~
总结
本文有种复习Spring Framework的赶脚,因为大部分概念都是“老的”,但也是最为核心的。以上概念多多少少都会和整合扯上点关系,后面会有所体现。
为何我觉得概念理解如此重要?我觉得概念就像是用于沟通的语言,只有做到感念一致性沟通起来才能顺畅,因此我才觉得在系列开头穿插着书写本文是很有必要的,磨刀不误砍柴工嘛。
尴尬爆料:今天猛然发现,之前好多文章里我把Environment单词写错了,写为了Enviroment,特此提醒各位不要“学我”哦