Spring Cloud学习——Spring Cloud Context

基于Dalston.SR4版
原文地址:Spring Cloud Context: Application Context Services

1. The Bootstrap Application Context

一个Spring Cloud应用通过创建一个bootstrap context运行起来,它是main application的父context。默认情况下,它负责从外部sources中加载configuration properties,同时也会解析本地外部配置文件。这两部分context共享一个EnvironmentEnvironment是所有Spring应用的外部属性配置的容器。Bootstrap properties有很高的优先级,所以在默认情况下不能被本地configuration覆盖。
Bootstrap context使用与main application context完全不同的方式去定位外部配置。所以在classpath下新建bootstrap.yml(.properties),从而优雅的将两种context的外部配置剥离。例如:

1
2
3
4
5
6
spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}

如果要禁用bootstrap进程,设置spring.cloud.bootstrap.enabled=false

2. Application Context Hierarchies

如果application context是从SpringApplication或者SpringApplicationBuilder启动的,那么Bootstrap context就作为它的父context被加入应用了。Spring有一个机制就是子context会继承父context的property sources and profiles,所以,与没有用Spring Cloud Config的应用相比,用了Spring Cloud Config的应用会多处一些额外的property sources:

  • “bootstrap”:如果在Bootstrap context中设置了PropertySourceLocators,应用中会生成一个高优先级的CompositePropertySource,其中有非空的properties。例如从Spring Cloud Config Server中加载的配置。另外也可以定制这个property source的内容。
  • “applicationConfig: [classpath:bootstrap.yml]”:如果定制了bootstrap.yml,那么其中的属性就被加载到Bootstrap context中。bootstrap.yml中的内容优先级要比application.yml中的,或者其他配置方式的低。

根据约定的property sources优先级排序规则,”bootstrap”优先级较高,但是要注意,并不包括bootstrap.yml中的配置。因为bootstrap.yml的优先级很低,并且可以被覆盖。但是可以在bootstrap.yml文件中设置默认值。
可以通过制定添加ApplicationContext父context的方式来扩展context机制,例如,可以使用它的接口,或者用SpringApplicationBuilder中的方法(parent(), child() and sibling())。Bootstrap context会作为所有自定义context的父context。在应用的Application Context Hierarchies中,每个context都有自己的”bootstrap”property source(可能为空),这种设定是为了避免其将值意外地从父context传递到子context。原则上每个context都能拥有自己的spring.application.name,进而如果有Config Server的话,可以有各自不同的property source。普通的Spring应用context配置规则遵循property resolution:子context可以覆盖父context的内容,替换规则是属性名和数据来源名(property source name)。

SpringApplicationBuilder可以在整个应用中通过Environment共享数据,但默认情况下并非如此,因此,在兄弟context中不需要有相同的profiles or property sources,尽管他们继承父context中的属性。

3. Changing the Location of Bootstrap Properties

原文链接🔗

4. Overriding the Values of Remote Properties

在bootstrap阶段,加入到application中的property sources默认是”remote”且不能覆盖的(e.g.Config Server),除了在命令行自动参数中。如果你想要在本地重写这些配置,远程property sourc必须要授权本地允许覆盖,通过设置spring.cloud.config.allowOverride=true参数实现。如果远端允许覆盖配置,那么还需要设置其他几个参数,以控制要重写那些参数,包括系统参数(System properties)和应用本地配置(local configuration):

  • spring.cloud.config.overrideNone=true`表示允许本地的任何配置覆盖remote配置。
  • spring.cloud.config.overrideSystemProperties=false`表示只有系统属性和环境变量能覆盖remote配置,但是本地配置文件不能覆盖remote配置。

5. Customizing the Bootstrap Configuration

bootstrap context可以通过配置来做到任何想实现的功能,实现方式是在/META-INF/spring.factories下配置入口,key为org.springframework.cloud.bootstrap.BootstrapConfiguration,值为一系列以半角逗号分隔的,可以创建context的,注释为@Configuration的类。所有想在main application context使用的Bean都可以在这里初始化。这些类可以通过配置@Order来控制其启动顺序(order的默认值是”last”)。

注意:在自定义BootstrapConfiguration时,不要让这些类被”main application”的@ComponentScanned错误的添加到其context中。可以在与main application context不同的目录下创建这些类。

bootstrap进程的最后一步,是将initializers注入到主SpringApplication实例中(i.e.正常的Spring Boot启动序列,不管作为独立的app还是一个应用服务端)。首先,bootstrap context从在spring.factories中标记的类中创建,然后,所有ApplicationContextInitializer类型的@BeanSpringApplication启动之前加载到其中。

6. Customizing the Bootstrap Property Sources

bootstrap process加载的默认property source是Config Server,但是可以通过在spring.factory中添加PropertySourceLocator来制定其他的property source。可以通过这种方式来从另一个服务器,或是一个数据库,或是另一个实例中加载配置。

e.g.

1
2
3
4
5
6
7
8
9
10
@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

@Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}

}

EnvironmentApplicationContext要创建的实例的其中之一,i.e.我们要在其中加入另外的property sources,它有正常的Spring Boot提供的默认的property sources,所以可以通过这个特性定位到这个Environment中的特定的property source(例如,通过spring.application.name来确定,这也是默认的Config Server property source locator的实现方式)。

在创建一个jar时,可以通过创建这样一个类,然后在META-INF/spring.factories中添加:

1
org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator

这样,”customProperty”PropertySource就可以被所有引入这个jar的工程使用。

7. Environment Changes

项目会通过EnvironmentChangeEvent来监听Environment的变化,并做出相应的反应(ApplicationListeners可以以@Bean的形式被用户添加使用),当EnvironmentChangeEvent被监听到以后,它其中会包含一系列发生变化的key-value对,应用会据此做出反应:

  • 重新加载所有context的@ConfigurationProperties
  • 给所有logging.level.*中标记的类设定logger levels

注意,Config Client默认情况下不会主动去拉Environment中的改变的值,一般也不建议通过检测变化的方式去实现(不过可以通过@Scheduled做定时任务)。如果在一个横向扩展的集群中,最好的实现方式是在集群中广播EnvironmentChangeEvent,而不是通知所有运行实例去拉(e.g. using the Spring Cloud Bus)。

EnvironmentChangeEvent中包含大量的use case,只要能在Environment中做调整并publish这个事件,可以通过绑定@ConfigurationProperties的Bean来校验数据(引入Spring Boot Actuator后,通过访问/configprops来查看)。重新构建@ConfigurationProperties不会覆盖另一个use case,因此在这些点需要比refresh更强的控制力,这些点在整个ApplicationContext中是一个原子的操作,为了实现这种操作,使用@RefreshScope

8. Refresh Scope

被标记为@RefreshScope的@Bean会在configuration改变时被特殊处理。刷新操作会使那些在创建时被注入配置的优状态的实例出现问题。例如,一个DatasourceEnvironment的database URL改变时还有打开的链接,我们会期望链接的holders会在我们刷新的时候去释放这些链接,当下一次从pool中取connection时能拿到一个新的链接。
Refresh scope的Bean时懒加载的,整个scope表现的像一个初始化参数的缓存。要使一个bean在下一次方法调用时强制重新初始化的话,只需要作废掉原始缓存的入口。

RefreshScope使context中的一个Bean,它有一个公共方法refreshAll(),用以清除所有scope中的scope目标缓存,另外也有一个refresh(String)方法,来根据名字去刷新指定的bean。这些功能通过/refresh节点暴露。

如果你喜欢我的blog,请鼓励我一杯咖啡☕️
0%