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

Spring Boot 对于如何使用 Spring 构建应用程序有一个坚定的看法。例如,它具有用于公共配置文件的常规位置,并具有用于公共 Management 和监视任务的端点。 Spring Cloud 在此基础上构建并添加了一些功能,这些功能可能是系统中所有组件可能会使用或偶尔需要的。

2.1 Bootstrap 应用程序上下文

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

引导上下文使用不同于主应用程序上下文的约定来定位外部配置。可以使用bootstrap.yml来代替application.yml(或.properties),从而使引导程序和主要上下文的外部配置很好地分开。以下清单显示了一个示例:

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来完全禁用引导过程(例如,在系统属性中)。

2.2 应用程序上下文层次结构

如果从SpringApplicationSpringApplicationBuilder构建应用程序上下文,则 Bootstrap 上下文将作为父级添加到该上下文。 Spring 的一个功能是子上下文从其父级继承属性源和配置文件,因此,与没有 Spring Cloud Config 的相同上下文相比,“主”应用程序上下文包含其他属性源。其他属性来源是:

  • “ bootstrap”:如果在 Bootstrap 上下文中找到任何PropertySourceLocators并且具有非空属性,则会以高优先级显示可选的CompositePropertySource。一个示例是 Spring Cloud Config Server 中的属性。有关如何自定义此属性源内容的说明,请参见“ 第 2.6 节“自定义 Bootstrap 属性源””。

  • “ applicationConfig:[classpath:bootstrap.yml]”(以及相关文件,如果 Spring 配置文件处于Active状态):如果您具有bootstrap.yml(或.properties),则这些属性用于配置 Bootstrap 上下文。然后,当它们的父级被设置时,它们被添加到子级上下文中。它们的优先级低于application.yml(或.properties)以及在创建 Spring Boot 应用程序过程中通常添加到子级的任何其他属性源的优先级。有关如何自定义这些属性源的内容的说明,请参见“ 第 2.3 节“更改引导程序属性的位置””。

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

您可以通过设置所创建的任何ApplicationContext的父上下文来扩展上下文层次结构,例如使用其自己的界面或使用SpringApplicationBuilder便捷方法(parent()child()sibling())。引导上下文是您创建的最高级祖先的父级。层次结构中的每个上下文都有其自己的“引导程序”(可能为空)属性源,以避免无意间将值从父级提升到子级。如果有配置服务器,则层次结构中的每个上下文原则上也可以具有不同的spring.application.name,因此也具有不同的远程属性源。常规的 Spring 应用程序上下文行为规则适用于属性解析:子上下文中的属性会按名称以及属性源名称覆盖父级属性。 (如果子项具有与父项同名的属性源,则子项中不包含来自父项的值)。

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

2.3 更改引导程序属性的位置

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

2.4 覆盖远程属性的值

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

  • spring.cloud.config.overrideNone=true:从任何本地属性源覆盖。

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

2.5 自定义引导程序配置

可以通过在名为org.springframework.cloud.bootstrap.BootstrapConfiguration的键下将条目添加到/META-INF/spring.factories来将引导上下文设置为执行您喜欢的任何事情。这包含用于创建上下文的 Spring @Configuration类的逗号分隔列表。您可以在此处创建要对主应用程序上下文可用以进行自动装配的任何 bean。 ApplicationContextInitializer类型的@Beans有特殊 Contract。如果要控制启动 Sequences,则可以用@Order注解标记类(默认 Sequences 为last)。

Warning

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

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

2.6 自定义 Bootstrap 属性源

引导过程添加的外部配置的默认属性源是 Spring Cloud Config Server,但是您可以通过将PropertySourceLocator类型的 bean 添加到引导上下文(通过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会出现在任何在其 Classpath 中包含该 jar 的应用程序中:

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

2.7 记录配置

如果要使用 Spring Boot 配置日志设置,则应将此配置放在`bootstrap。[yml |属性],如果您希望将其应用于所有事件。

Note

为了使 Spring Cloud 正确初始化日志记录配置,您不能使用自定义前缀。例如,初始化日志记录系统时,Spring Cloud 将无法识别使用custom.loggin.logpath

2.8 环境更改

该应用程序侦听EnvironmentChangeEvent并以几种标准方式对更改做出反应(其他ApplicationListeners可以由用户以正常方式添加为@Beans)。观察到EnvironmentChangeEvent时,它具有已更改的键值列表,应用程序使用这些键值来:

  • 重新绑定上下文中的任何@ConfigurationProperties bean

  • logging.level.*中的所有属性设置 Logger 级别

请注意,默认情况下,Config Client 不轮询Environment中的更改。通常,我们不建议您使用这种方法来检测更改(尽管您可以使用@ScheduledComments 对其进行设置)。如果您具有横向扩展的 Client 端应用程序,则最好将EnvironmentChangeEventBroadcast 到所有实例,而不是让它们轮询更改(例如,使用Spring Cloud 巴士)。

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

2.9 刷新范围

进行配置更改时,标记为@RefreshScope的 Spring @Bean将得到特殊处理。此功能解决了有状态 Bean 的问题,这些状态 Bean 仅在初始化时才注入配置。例如,如果在通过Environment更改数据库 URL 时DataSource具有打开的连接,则您可能希望这些连接的所有者能够完成它们的工作。然后,下次某物从池中借用连接时,它将获得具有新 URL 的连接。

有时,甚至可能必须将@RefreshScopeComments 应用于只能初始化一次的某些 bean。如果 bean 是“不可变的”,则必须用@RefreshScopeCommentsbean 或在属性键spring.cloud.refresh.extra-refreshable下指定类名。

Tip

如果您自己创建一个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

Note

@RefreshScope(在技术上)适用于@Configuration类,但可能会导致令人惊讶的行为。例如,这并不意味着该类中定义的所有@Beans本身都在@RefreshScope中。具体而言,除非刷新本身位于@RefreshScope中,否则任何依赖于这些 bean 的东西都不能依赖于刷新启动时对其进行更新。在这种情况下,将在刷新时重建它,并重新注入其依赖项。此时,它们将从刷新的@Configuration重新初始化。

2.10 加密和解密

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

如果由于“密钥大小非法”而导致异常,并且使用 Sun 的 JDK,则需要安装 Java 密码术扩展(JCE)无限强度管辖权策略文件。有关更多信息,请参见以下链接:

将文件解压缩到所使用的 JRE/JDK x64/x86 版本的 JDK/jre/lib/security 文件夹中。

2.11 Endpoints

对于 Spring Boot Actuator 应用程序,可以使用一些其他 Management 端点。您可以使用:

  • POST/actuator/env以更新Environment并重新绑定@ConfigurationProperties和日志级别。

  • /actuator/refresh重新加载引导上下文并刷新@RefreshScope bean。

  • /actuator/restart关闭ApplicationContext并重新启动它(默认情况下处于禁用状态)。

  • /actuator/pause/actuator/resume用于调用Lifecycle方法(ApplicationContext上的stop()start())。

Note

如果禁用/actuator/restart端点,则/actuator/pause/actuator/resume端点也将被禁用,因为它们只是/actuator/restart的特例。