1. Spring Cloud Context:应用程序上下文服务
Spring Boot 对如何使用 Spring 构建应用程序有约定优于配置的方式。 例如,它具有用于通用配置文件的常规位置,并具有用于通用管理和监控任务的端点。 Spring Cloud 构建在此基础上,并添加了系统中许多组件会使用或偶尔需要的一些功能。
1.1. Bootstrap 应用程序上下文
Spring Cloud 应用程序通过创建“引导”上下文来运行,该上下文是主应用程序的父上下文。
此上下文负责从外部源加载配置属性,并解密本地外部配置文件中的属性。
这两个上下文共享一个Environment
,这是任何 Spring 应用程序的外部属性的来源。
默认情况下,bootstrap 属性(不是bootstrap.properties
但在引导阶段加载的属性)以高优先级添加,因此它们不能被本地配置覆盖。
引导上下文使用与主应用程序上下文不同的约定来查找外部配置。
而不是application.yml
(或.properties
),您可以使用bootstrap.yml
,将 bootstrap 和 main 上下文的外部配置很好地分开。
以下列表显示了一个示例:
spring: application: name: foo cloud: config: uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果您的应用程序需要服务器中任何特定于应用程序的配置,最好将spring.application.name
(在bootstrap.yml
或application.yml
).
对于物业spring.application.name
要用作应用程序的上下文 ID,您必须将其设置为bootstrap.[properties | yml]
.
如果要检索特定的配置文件配置,还应将spring.profiles.active
在bootstrap.[properties | yml]
.
您可以通过将spring.cloud.bootstrap.enabled=false
(例如,在系统属性中)。
1.2. 应用程序上下文层次结构
如果从SpringApplication
或SpringApplicationBuilder
,则 Bootstrap 上下文将作为父级添加到该上下文。
Spring 的一个特性是,子上下文从其父上下文继承属性源和配置文件,因此与在没有 Spring Cloud Config 的情况下构建相同的上下文相比,“主”应用程序上下文包含额外的属性源。
其他属性来源包括:
-
“bootstrap”:如果有的话
PropertySourceLocators
在引导上下文中找到,如果它们具有非空属性,则可选的CompositePropertySource
以高优先级出现。 一个例子是来自 Spring Cloud Config Server 的属性。 有关如何自定义此属性源的内容,请参阅“自定义 Bootstrap 属性源”。
在 Spring Cloud 2021.0.8 之前PropertySourceLocators (包括用于 Spring Cloud Config 的那些)在
主应用程序上下文,而不是在 Bootstrap 上下文中。您可以强制PropertySourceLocators 在
通过设置 Bootstrap 上下文spring.cloud.config.initialize-on-context-refresh=true 在bootstrap.[properties | yaml] . |
-
“applicationConfig: [classpath:bootstrap.yml]”(如果 Spring 配置文件处于活动状态,则为相关文件):如果您有
bootstrap.yml
(或.properties
),这些属性用于配置引导上下文。 然后,当设置父上下文时,它们将添加到子上下文中。 它们的优先级低于application.yml
(或.properties
)以及作为创建 Spring Boot 应用程序过程的正常部分添加到子项的任何其他属性源。有关如何自定义这些属性源的内容,请参阅“更改 Bootstrap 属性的位置”。
由于属性源的排序规则,“引导”条目优先。但是,请注意,这些条目不包含来自bootstrap.yml
,其优先级非常低,但可用于设置默认值。
您可以通过设置任何ApplicationContext
例如,通过使用自己的接口或使用SpringApplicationBuilder
方便方法(parent()
,child()
和sibling()
).
引导上下文是您自己创建的最高级祖先的父级。
层次结构中的每个上下文都有自己的“引导”(可能是空的)属性源,以避免无意中将价值观从父母推广到他们的后代。
如果有配置服务器,则层次结构中的每个上下文也可以(原则上)具有不同的spring.application.name
因此,远程属性源不同。
正常的 Spring 应用程序上下文行为规则适用于属性解析:子上下文中的属性覆盖了
父级,按名称和属性源名称。
(如果子项具有与父项同名的属性源,则父项中的值不会包含在子项中)。
请注意,SpringApplicationBuilder
允许您共享Environment
在整个层次结构中,但这不是默认设置。
因此,同级上下文(特别是)不需要具有相同的配置文件或属性源,即使它们可能与其父级共享共同值。
1.3. 更改 Bootstrap 属性的位置
这bootstrap.yml
(或.properties
)位置可以通过设置来指定spring.cloud.bootstrap.name
(默认值:bootstrap
),spring.cloud.bootstrap.location
(默认值:空)或spring.cloud.bootstrap.additional-location
(默认值:空)——例如,在系统属性中。
这些属性的行为类似于spring.config.*
具有相同名称的变体。
跟spring.cloud.bootstrap.location
默认位置将被替换,并且仅使用指定的位置。
要将位置添加到默认位置列表中,spring.cloud.bootstrap.additional-location
可以使用。
事实上,它们是用来设置引导的ApplicationContext
通过在其Environment
.
如果存在活动配置文件(从spring.profiles.active
或通过Environment
API),该配置文件中的属性也会被加载,与常规 Spring Boot 应用程序中相同——例如,从bootstrap-development.properties
对于一个development
轮廓。
1.4. 覆盖远程属性的值
通过引导上下文添加到应用程序的属性源通常是“远程”的(例如,来自 Spring Cloud Config Server)。默认情况下,它们不能在本地覆盖。如果您想让您的应用程序使用自己的系统属性或配置文件覆盖远程属性,则远程属性源必须通过设置spring.cloud.config.allowOverride=true
(在本地设置此设置是行不通的)。
设置该标志后,两个更细粒度的设置将控制远程属性与系统属性和应用程序本地配置相关的位置:
-
spring.cloud.config.overrideNone=true
:从任何本地属性源覆盖。 -
spring.cloud.config.overrideSystemProperties=false
:只有系统属性、命令行参数和环境变量(而不是本地配置文件)应该覆盖远程设置。
1.5. 自定义引导配置
可以通过将条目添加到/META-INF/spring.factories
在名为org.springframework.cloud.bootstrap.BootstrapConfiguration
.
这包含一个以逗号分隔的 Spring 列表@Configuration
用于创建上下文的类。
您可以在此处创建任何您希望可用于主应用程序上下文以进行自动装配的 Bean。
有特殊合同@Beans
类型ApplicationContextInitializer
.
如果要控制启动顺序,可以使用@Order
注释(默认顺序为last
).
添加自定义时BootstrapConfiguration ,请注意您添加的类不是@ComponentScanned 错误地进入您的“主”应用程序上下文,在那里可能不需要它们。
为引导配置类使用单独的包名称,并确保该名称尚未被@ComponentScan 或@SpringBootApplication 带注释的配置类。 |
引导过程通过将初始值设定项注入主SpringApplication
实例(这是正常的 Spring Boot 启动序列,无论它是作为独立应用程序运行还是部署在应用程序服务器中)。首先,从spring.factories
. 然后,所有@Beans
类型ApplicationContextInitializer
添加到主SpringApplication
在开始之前。
1.6. 自定义 Bootstrap 属性源
引导过程添加的外部配置的默认属性源是 Spring Cloud Config Server,但您可以通过添加类型为PropertySourceLocator
到 Bootstrap 上下文(通过spring.factories
). 例如,您可以从其他服务器或数据库插入其他属性。
例如,请考虑以下自定义定位器:
@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"));
}
}
这Environment
传入的就是ApplicationContext
即将创建——换句话说,我们为其提供其他属性源的属性源。它已经具有正常的 Spring Boot 提供的属性源,因此您可以使用这些属性源来定位特定于此属性源Environment
(例如,通过键入它spring.application.name
,就像在默认的 Spring Cloud Config Server 属性源定位器中所做的那样)。
如果您创建一个包含此类的 jar,然后添加一个META-INF/spring.factories
包含以下设置,customProperty
PropertySource
出现在其类路径上包含该 jar 的任何应用程序中:
org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
从 Spring Cloud 2021.0.8 开始,Spring Cloud 现在将调用PropertySourceLocators
两次。 第一次获取将检索任何没有任何配置文件的属性源。这些属性源将有机会使用spring.profiles.active
. 主应用程序上下文启动后PropertySourceLocators
将第二次调用,这次使用任何活动配置文件允许PropertySourceLocators
定位任何其他PropertySources
与配置文件。
1.7. 日志记录配置
如果使用 Spring Boot 配置日志设置,则应将此配置放在bootstrap.[yml | properties]
如果您希望它适用于所有事件。
为了让 Spring Cloud 正确初始化日志记录配置,不能使用自定义前缀。例如,使用custom.loggin.logpath 在初始化日志记录系统时,Spring Cloud 无法识别。 |
1.8. 环境变化
应用程序监听EnvironmentChangeEvent
并以几种标准方式对更改做出反应(额外的ApplicationListeners
可以添加为@Beans
以正常方式)。当EnvironmentChangeEvent
,它有一个已更改的键值列表,应用程序使用这些键值来:
-
重新绑定任何
@ConfigurationProperties
bean 在上下文中。 -
设置中任何属性的记录器级别
logging.level.*
.
请注意,默认情况下,Spring Cloud Config Client 不会轮询Environment
. 通常,我们不建议使用这种方法来检测更改(尽管您可以使用@Scheduled
注释)。如果您有横向扩展的客户端应用程序,最好广播EnvironmentChangeEvent
到所有实例,而不是让它们轮询更改(例如,通过使用 Spring Cloud Bus)。
这EnvironmentChangeEvent
涵盖了一大类刷新用例,只要您实际上可以对Environment
并发布事件。请注意,这些 API 是公共的,并且是核心 Spring 的一部分)。您可以验证更改是否绑定到@ConfigurationProperties
bean 通过访问/configprops
endpoint(标准的 Spring Boot Actuator 功能)。例如,一个DataSource
可以拥有它的maxPoolSize
在运行时更改(默认DataSource
由 Spring Boot 创建的是一个@ConfigurationProperties
bean)并动态增长容量。重新绑定@ConfigurationProperties
不涵盖另一类用例,在这些用例中,您需要对刷新进行更多控制,并且需要更改以对整体进行原子化ApplicationContext
. 为了解决这些担忧,我们@RefreshScope
.
1.9. 刷新作用域
当配置发生更改时,Spring@Bean
标记为@RefreshScope
得到特殊待遇。
此功能解决了有状态 Bean 仅在初始化时注入其配置的问题。
例如,如果DataSource
当数据库 URL 通过Environment
,您可能希望这些连接的持有者能够完成他们正在做的事情。
然后,下次从池中借用连接时,它会获得一个带有新 URL 的连接。
有时,甚至可能强制应用@RefreshScope
注释,这些 bean 只能初始化一次。
如果一个 bean 是“不可变的”,你必须用@RefreshScope
或在属性键下指定类名:spring.cloud.refresh.extra-refreshable
.
如果你有一个DataSource bean 的HikariDataSource ,不能
刷新。它是spring.cloud.refresh.never-refreshable .选择一个
不同DataSource 实现,如果需要刷新它。 |
刷新作用域 Bean 是惰性代理,在使用它们时(即调用方法时)进行初始化,作用域充当初始化值的缓存。 要强制 Bean 在下一个方法调用时重新初始化,您必须使其缓存条目失效。
这RefreshScope
是上下文中的 bean,并且有一个公共的refreshAll()
方法通过清除目标缓存来刷新作用域中的所有 bean。
这/refresh
endpoint 公开此功能(通过 HTTP 或 JMX)。
要按名称刷新单个 Bean,还有一个refresh(String)
方法。
要公开/refresh
端点时,您需要向应用程序添加以下配置:
management:
endpoints:
web:
exposure:
include: refresh
@RefreshScope (从技术上讲)适用于@Configuration 类,但它可能会导致令人惊讶的行为。
例如,这并不意味着所有@Beans 在该类中定义的@RefreshScope .
具体来说,任何依赖于这些 bean 的东西都不能依赖于在启动刷新时更新它们,除非它本身位于@RefreshScope .
在这种情况下,它会在刷新时重新构建,并重新注入其依赖项。
此时,它们将从刷新的@Configuration ). |
删除配置值然后执行刷新不会更新配置值的存在。 必须存在配置属性才能在刷新后更新值。如果您依赖 应用程序中的值,您可能希望切换逻辑以依赖它的缺失。另一种选择是依靠 值发生变化,而不是不存在于应用程序的配置中。 |
1.10. 加密和解密
Spring Cloud 有一个Environment
用于在本地解密属性值的预处理器。
它遵循与 Spring Cloud Config Server 相同的规则,并通过encrypt.*
.
因此,您可以使用以下形式的加密值{cipher}*
,并且只要存在有效的密钥,它们就会在主应用程序上下文获得Environment
设置。
要在应用程序中使用加密功能,您需要在类路径中包含 Spring Security RSA(Maven 坐标:org.springframework.security:spring-security-rsa
),并且您还需要在 JVM 中提供完整的 JCE 扩展。
如果您由于“密钥大小非法”而获得异常,并且您使用 Sun 的 JDK,则需要安装 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。 有关详细信息,请参阅以下链接:
将文件解压到您使用的 JRE/JDK x64/x86 版本的 JDK/jre/lib/security 文件夹中。
1.11. 端点
对于 Spring Boot Actuator 应用程序,可以使用一些额外的管理端点。您可以使用:
-
POST
自/actuator/env
更新Environment
并重新绑定@ConfigurationProperties
和日志级别。 要启用此终结点,您必须将management.endpoint.env.post.enabled=true
. -
/actuator/refresh
重新加载引导带上下文并刷新@RefreshScope
豆。 -
/actuator/restart
关闭ApplicationContext
并重新启动它(默认禁用)。 -
/actuator/pause
和/actuator/resume
用于调用Lifecycle
方法 (stop()
和start()
在ApplicationContext
).
如果您禁用/actuator/restart endpoint,然后/actuator/pause 和/actuator/resume 端点
也将被禁用,因为它们只是/actuator/restart . |