1

Spring Cloud Context:应用程序上下文服务

Spring Boot有一个关于如何使用Spring构建应用程序的主见,例如,它具有通用配置文件的常规位置,并具有用于通用管理和监控任务的端点,Spring Cloud在此基础上构建并添加了一些功能,这些功能可能是系统中的所有组件都会使用或偶尔需要的。

Bootstrap应用程序上下文

Spring Cloud应用程序通过创建“bootstrap”上下文来运转,该上下文是主应用程序的父上下文,它负责从外部源加载配置属性以及用于解密本地外部配置文件中的属性,这两个上下文共享一个Environment,它是任何Spring应用程序的外部属性来源。默认情况下,bootstrap属性(不是bootstrap.properties,而是在引导阶段加载的属性)以高优先级添加,因此本地配置无法覆盖它们。

bootstrap上下文使用与主应用程序上下文不同的约定来定位外部配置,你可以使用bootstrap.yml而不是application.yml(或.properties),保持bootstrap和主上下文的外部配置很好地分开,以下清单显示了一个示例:

bootstrap.yml
spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}

如果你的应用程序需要来自服务器的任何特定于应用程序的配置,则最好设置spring.application.name(在bootstrap.ymlapplication.yml中)。

你可以通过设置spring.cloud.bootstrap.enabled=false来完全禁用引导过程(例如,在系统属性中)。

应用程序上下文层次结构

如果你从SpringApplicationSpringApplicationBuilder构建应用程序上下文,那么Bootstrap上下文将添加为该上下文的父级。Spring的一个特性是子上下文从其父级继承属性源和配置文件,因此与不使用Spring Cloud Config构建相同的上下文相比,“主”应用程序上下文包含额外的属性源,额外的属性源是:

  • “bootstrap”:如果在Bootstrap上下文中找到任何PropertySourceLocators,并且它们具有非空属性,则会出现具有高优先级的可选CompositePropertySource,一个例子是Spring Cloud Config Server的属性。
  • “applicationConfig: [classpath:bootstrap.yml]”(以及相关文件,如果Spring配置文件处于活动状态):如果你有bootstrap.yml(或.properties),则这些属性用于配置Bootstrap上下文,然后,当设置了其父级时,它们将添加到子上下文中。它们的优先级低于application.yml(或.properties)以及作为创建Spring Boot应用程序过程的正常部分添加到子级的任何其他属性源。

由于属性源的排序规则,“bootstrap”条目优先,但请注意,这些不包含来自bootstrap.yml的任何数据,它具有非常低的优先级,但可用于设置默认值。

你可以通过设置你创建的任何ApplicationContext的父上下文来扩展上下文层次结构 — 例如,通过使用它自己的接口或SpringApplicationBuilder便捷方法(parent()child()sibling()),bootstrap上下文是你自己创建的最高级祖先的父级。层次结构中的每个上下文都有自己的“bootstrap”(可能是空的)属性源,以避免无意中把父级的值传给后代。如果存在Config Server,则层次结构中的每个上下文(原则上)也可以具有不同的spring.application.name,因此具有不同的远程属性源。普通的Spring应用程序上下文行为规则适用于属性解析:来自子上下文的属性按名称和属性源名称覆盖父级中的属性(如果子项具有与父项具有相同名称的属性源,则父项中的值不包括在子项中)。

请注意,SpringApplicationBuilder允许你在整个层次结构中共享Environment,但这不是默认设置,因此,兄弟上下文尤其不需要具有相同的配置文件或属性源,即使他们可能与父上下文共享共同的值。

更改Bootstrap属性的位置

可以通过设置spring.cloud.bootstrap.name(默认值:bootstrap)或spring.cloud.bootstrap.location(默认值为空)来指定bootstrap.yml(或.properties)位置 — 例如,在系统属性中。这些属性的行为类似于具有相同名称的spring.config.*变体,实际上,它们用于通过在其Environment中设置这些属性来设置bootstrap ApplicationContext。如果存在活动配置文件(来自spring.profiles.active或通过你正在构建的上下文中的Environment API),则该配置文件中的属性也会加载,与常规Spring Boot应用程序相同 — 例如,来自bootstrap-development.propertiesdevelopment配置文件。

覆盖远程属性的值

bootstrap上下文添加到应用程序的属性源通常是“远程”的(例如,来自Spring Cloud Config Server),默认情况下,它们无法被本地覆盖。如果要让应用程序使用自己的系统属性或配置文件覆盖远程属性,远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予它权限(它不能在本地设置它),设置该标志后,两个更细粒度的设置将控制远程属性相对于系统属性和应用程序本地配置的位置:

  • spring.cloud.config.overrideNone=true:从任何本地属性源覆盖。
  • spring.cloud.config.overrideSystemProperties=false:只有系统属性、命令行参数和环境变量(但不是本地配置文件)才应覆盖远程设置。

自定义Bootstrap配置

通过向/META-INF/spring.factories下添加键名为org.springframework.cloud.bootstrap.BootstrapConfiguration的条目,可以将bootstrap上下文设置为执行任何操作。它包含一个以逗号分隔的用于创建上下文的Spring @Configuration类列表,你可以在此处创建你希望可用于主应用程序上下文以进行自动装配的任何Bean。ApplicationContextInitializer类型的@Beans有一个特殊的合约,如果要控制启动顺序,可以使用@Order注解标记类(默认顺序为last)。

添加自定义BootstrapConfiguration时,请注意你添加的类不是@ComponentScanned错误地进入你的“主”应用程序上下文,可能不需要它们。为引导配置类使用单独的包名称,并确保@ComponentScan@SpringBootApplication注解的配置类尚未涵盖该名称。

引导过程通过将初始化器注入主SpringApplication实例(这是正常的Spring Boot启动顺序,无论是作为独立应用程序运行还是部署在应用程序服务器中)而结束,首先,从spring.factories中的类创建bootstrap上下文,然后,所有类型为ApplicationContextInitializer@Beans在它启动之前都会添加到主SpringApplication中。

自定义Bootstrap属性源

引导过程添加的外部配置的默认属性源是Spring Cloud Config Server,但你可以通过将类型为PropertySourceLocator的bean添加到bootstrap上下文中来添加其他来源(通过spring.factories),例如,你可以从不同的服务器或数据库插入其他属性。

例如,请考虑以下自定义locator

@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 Boot配置日志设置,则应将此配置放在‘bootstrap.[yml | properties]’,如果你希望它适用于所有事件。

要使Spring Cloud正确初始化日志记录配置,你不能使用自定义前缀,例如,初始化日志记录系统时,使用custom.loggin.logpath不被Spring Cloud识别。

环境变化

应用程序侦听EnvironmentChangeEvent并以几种标准方式对更改做出反应(用户可以正常方式将其他ApplicationListener添加为@Beans),当观察到EnvironmentChangeEvent时,它有一个已更改的键值列表,应用程序使用它们:

  • 在上下文中重新绑定任何@ConfigurationProperties bean
  • logging.level.*中的任何属性设置记录器级别

请注意,默认情况下,Config Client不会轮询Environment中的更改,通常,我们不建议使用这种方法来检测更改(尽管你可以使用@Scheduled注解进行设置)。如果你有一个扩展的客户端应用程序,最好将EnvironmentChangeEvent广播到所有实例,而不是让它们轮询更改(例如,通过使用Spring Cloud Bus)。

EnvironmentChangeEvent涵盖了一大类刷新用例,只要你可以实际更改环境并发布事件即可,请注意,这些API是公共的,并且是核心Spring的一部分。你可以通过访问/configprops端点(一个正常的Spring Boot Actuator功能)来验证更改是否绑定到@ConfigurationProperties bean。例如,DataSource可以在运行时更改其maxPoolSize(Spring Boot创建的默认DataSource@ConfigurationProperties bean)并动态增加容量。重新绑定@ConfigurationProperties不包括另一大类用例,这类用例你需要更多地控制刷新,并且需要对整个ApplicationContext进行原子性更改,为了解决这些问题,我们有@RefreshScope

刷新作用域

当配置发生变化时,标记为@RefreshScope的Spring @Bean会得到特殊处理,该特性解决了只有在初始化状态bean时才注入配置的有状态bean的问题。例如,如果DataSource在通过Environment更改数据库URL时具有打开的连接,你可能希望这些连接的持有者能够完成他们正在做的事情,然后,下次从池中借用某个连接时,它会获得一个带有新URL的连接。

有时,甚至可能必须在某些只能初始化一次的bean上应用@RefreshScope注解,如果bean是“不可变的”,则必须使用@RefreshScope注解bean,或者在属性键spring.cloud.refresh.extra-refreshable下指定classname

如果你自己创建一个DataSource bean并且该实现是一个HikariDataSource,则返回最具体的类型,在本例中为HikariDataSource,否则,你需要设置spring.cloud.refresh.extra-refreshable=javax.sql.DataSource

刷新作用域bean是在使用它们时初始化的惰性代理(即,在调用方法时),并且作用域充当初始化值的缓存,要强制bean在下一个方法调用上重新初始化,必须使其缓存条目无效。

RefreshScope是上下文中的一个bean,它有一个公共refreshAll()方法,通过清除目标缓存来刷新作用域中的所有bean,/refresh端点公开此功能(通过HTTP或JMX),要按名称刷新单个bean,还有一个refresh(String)方法。

要公开/refresh端点,你需要将以下配置添加到你的应用程序:

management:
  endpoints:
    web:
      exposure:
        include: refresh
@RefreshScope(在技术上)在@Configuration类上工作,但它可能会导致令人惊讶的行为。例如,它并不意味着该类中定义的所有@Beans本身都在@RefreshScope中,具体来说,依赖于那些bean的任何东西都不能依赖于在启动刷新时更新它们,除非它本身在@RefreshScope中。在这种情况下,它会在刷新时重建,并重新注入其依赖项,此时,它们将从刷新的@Configuration重新初始化。

加密和解密

Spring Cloud有一个Environment预处理器,用于在本地解密属性值,它遵循与Config Server相同的规则,并通过encrypt.*具有相同的外部配置。因此,你可以使用{cipher}*形式的加密值,只要有有效密钥,它们就会在主应用程序上下文获取Environment设置之前被解密。要在应用程序中使用加密功能,你需要在类路径中包含Spring Security RSA(Maven:"org.springframework.security:spring-security-rsa"),并且你还需要JVM中的全强度JCE扩展。

如果由于“Illegal key size”而导致异常,并且你使用Sun的JDK,则需要安装Java加密扩展(JCE)无限制强度权限策略文件,有关更多信息,请参阅以下链接:

无论你使用哪种版本的JRE/JDK x64/x86,都要将文件解压缩到JDK/jre/lib/security文件夹中。

端点

对于Spring Boot Actuator应用程序,可以使用一些其他管理端点,你可以使用:

  • POST/actuator/env以更新Environment并重新绑定@ConfigurationProperties和日志级别。
  • /actuator/refresh重新加载引导上下文并刷新@RefreshScopebean。
  • /actuator/restart关闭ApplicationContext并重新启动它(默认情况下禁用)。
  • /actuator/pause/actuator/resume用于调用Lifecycle方法(ApplicationContext上的stop()start())。
如果禁用/actuator/restart端点,则/actuator/pause/actuator/resume端点也将被禁用,因为它们只是/actuator/restart的特殊情况。

上一篇:Spring Cloud 参考文档(目录)
下一篇:Spring Cloud Commons:通用的抽象

博弈
2.5k 声望1.5k 粉丝

态度决定一切