使用 Spring Boot 在 Cloud Foundry 中绑定数据服务

工程 | Dave Syer | 2015 年 4 月 27 日 | ...

在本文中,我们将探讨如何将Spring Boot应用绑定到数据服务(JDBC、NoSQL、消息传递等),以及Cloud Foundry中各种默认行为和自动行为的来源,并提供关于何时使用哪种行为以及在何种条件下行为会激活的一些指导。Spring Boot 提供了许多自动配置和外部绑定功能,其中一些与 Cloud Foundry 相关,但许多则不相关。Spring Cloud Connectors 是一个库,如果您想以编程方式创建自己的组件,可以在应用程序中使用它,但它本身不会做任何“神奇”的事情。最后,还有 Cloud Foundry Java buildpack,它具有一个“自动重新配置”功能,旨在减轻将简单应用迁移到云端的负担。正确配置中间件服务(如 JDBC、AMQP 或 Mongo)的关键是了解这些工具各提供什么,它们在运行时如何相互影响,以及如何打开和关闭其中的部分功能。目标应该是应用程序从开发者桌面上的本地执行平稳过渡到 Cloud Foundry 中的测试环境,最终按照十二因素应用程序指南,在 Cloud Foundry(或其他环境)中进行生产部署,而无需更改源代码或打包。

本文附带了一些简单的源代码。要使用它,您可以克隆仓库并导入到您喜欢的 IDE 中。您需要从完整项目中移除两个依赖项才能达到我们开始讨论具体代码示例时的同一点,即spring-boot-starter-cloud-connectorsauto-reconfiguration

注意:本文讨论的所有库的当前坐标为org.springframework.boot:spring-boot-*:2.2.5.RELEASEorg.springframework.cloud:spring-cloud-*-connector:1.0.8.RELEASEorg.cloudfoundry:auto-reconfiguration:2.11.0.RELEASE

提示:github 中的源代码包含一个docker-compose.yml文件。如果您还没有运行本地 MySQL 数据库,可以使用它来创建一个。实际上,运行下面大多数代码并不需要它,但用来验证其是否确实能工作可能会很有用。

不耐烦的读者看这里

如果您想跳过细节,只需要一个在本地使用 H2、在云端使用 MySQL 的方案,那么请从这里开始阅读,稍后当您想更深入地了解时再阅读其余部分。(其他数据服务,如 RabbitMQ、Redis、Mongo 等,也有类似的选项。)

您的第一个也是最简单的选择是完全不做任何事情:根本不定义DataSource,但将 H2 放在 classpath 中。当您在本地运行时,Spring Boot 会为您创建 H2 嵌入式DataSource。当您在云端运行时,Cloud Foundry buildpack 会检测到数据库服务绑定并为您创建一个DataSource。如果您也添加了 Spring Cloud Connectors,只要您包含了连接器,您的应用程序也可以在其他云平台上工作。如果您只想让某些东西运行起来,这可能就足够了。

如果您想在生产环境中运行一个严肃的应用程序,您可能需要调整一些连接池设置(例如,池的大小、各种超时、重要的借用测试标记)。在这种情况下,buildpack 的自动重新配置DataSource将无法满足您的要求,您需要选择一个替代方案,并且有多种或多或少合理的选择。

最简单的选择是设置一个环境变量 JBP_CONFIG_SPRING_AUTO_RECONFIGURATION='{ enabled: false }',并使用 spring.datasource.* 属性(例如在 application.properties 或其特定配置文件版本中)在运行时设置额外的属性。构建包会自动为你激活“cloud”配置文件。例如:

spring.datasource.url: ${vcap.services.mysql.credentials.jdbcUrl:jdbc:h2:mem:testdb}
spring.datasource.username: ${vcap.services.mysql.credentials.username:sa}
spring.datasource.password: ${vcap.services.mysql.credentials.password:}
spring.datasource.testOnBorrow: true

加上您需要的任何其他spring.datasource.*属性。

另一个选择是使用 Spring Cloud Connectors 显式创建一个 DataSource,但要受“cloud”配置文件的保护。

@Configuration
@Profile("cloud")
public class DataSourceConfiguration {

  @Bean
  public Cloud cloud() {
    return new CloudFactory().getCloud();
  }

  @Bean
  @ConfigurationProperties(DataSourceProperties.PREFIX)
  public DataSource dataSource() {
    return cloud().getSingletonServiceConnector(DataSource.class, null);
  }

}

现在来详细讲解。我们需要构建一个关于您的应用程序在运行时发生的情况的图景,这样我们就可以从中学习如何为配置数据服务做出明智的选择。

自动配置的层次

让我们来看一个带有 DataSource 的简单应用程序(RabbitMQ、Mongo、Redis 也适用类似考虑)

@SpringBootApplication
public class CloudApplication {
	
	@Autowired
	private DataSource dataSource;
	
	public static void main(String[] args) {
		SpringApplication.run(CloudApplication.class, args);
	}

}

这是一个完整的应用程序:DataSource可以通过@Autowired注入,因为它由 Spring Boot 为我们创建。DataSource的详细信息(具体类、JDBC 驱动、连接 URL 等)取决于 classpath 中的内容。让我们假设应用程序通过spring-boot-starter-jdbc(或spring-boot-starter-data-jpa)使用 Spring JDBC,因此即使它不是 web 应用程序,classpath 中也有一个来自 Tomcat 的DataSource实现可用,这就是 Spring Boot 使用的。

考虑以下情况会发生什么

  • Classpath 中只有 H2(除了 starter 之外):DataSource是来自DataSourceAutoConfiguration的 Tomcat 高性能连接池,它连接到内存数据库“testdb”。

  • Classpath 中有 H2 和 MySQL:DataSource仍然是 H2(与之前相同),因为我们没有为 MySQL 提供任何额外配置,Spring Boot 无法猜测连接凭据。

  • spring-boot-starter-cloud-connectors添加到 classpath:DataSource没有变化,因为 Spring Cloud Connectors 没有检测到它们正在云平台上运行。starter 中提供的所有提供者都查找特定的环境变量,如果您不设置它们,或者不在 Cloud Foundry、Heroku 等环境中运行应用程序,它们将找不到这些变量。

  • 在“cloud”配置文件中运行,使用spring.profiles.active=cloudDataSource还没有变化,但这是Java buildpack在您的应用程序运行在 Cloud Foundry 中时会做的事情之一。

  • 在“cloud”配置文件中运行,并提供一些环境变量来模拟在 Cloud Foundry 中运行并绑定到 MySQL 服务

VCAP_APPLICATION={"name":"application","instance_id":"FOO"}
VCAP_SERVICES={"mysql":[{"name":"mysql","tags":["mysql"],"credentials":{"uri":"mysql://root:root@localhost/test"}}]}

DataSource现在使用 MySQL,凭据由VCAP_*环境变量提供。Spring Boot 对 Connectors 进行了一些自动配置,所以如果您查看应用程序中的 bean,您会看到一个CloudFactory bean,还有一个DataSource bean(ID 为“mysql”)。自动配置等同于在您的应用程序配置中添加@ServiceScan。它仅在您的应用程序运行在“cloud”配置文件中,并且没有类型为Cloud的现有@Bean,以及配置标志spring.cloud.enabled不为“false”时才激活。

  • 添加来自 Java buildpack 的“auto-reconfiguration”JAR(Maven 坐标为org.cloudfoundry:auto-reconfiguration:2.11.0.RELEASE)。您可以将其添加为本地依赖项来模拟在 Cloud Foundry 中运行,但这对于实际应用程序来说并不正常(这仅用于试验自动配置)。自动重新配置 JAR 现在拥有创建DataSource所需的一切,但它还没有这样做(暂时还没有),因为它检测到您已经有一个类型为CloudFactory的 bean,这个 bean 是由 Spring Boot 添加的。

  • 移除显式的“cloud”配置文件。Buildpack JAR 会再次激活此配置文件。DataSource仍然没有变化,因为 Spring Boot 通过@ServiceScan为您创建了它。

  • 移除spring-boot-starter-cloud-connectors依赖项,这样 Spring Boot 就会停止创建CloudFactory。自动重新配置 JAR 实际上有它自己的 Spring Cloud Connectors 副本(所有类的包名不同),它现在使用它们来创建DataSource(在一个BeanFactoryPostProcessor中)。Spring Boot 自动配置的DataSource被替换为一个通过VCAP_SERVICES绑定到 MySQL 的 DataSource。无法控制连接池属性,但如果可用,它仍然使用 Tomcat 连接池(不支持 Hikari 或 DBCP2)。

  • 移除 auto-reconfiguration JAR,DataSource就会恢复为 H2。

提示:使用 web 和 actuator starter,并设置endpoints.health.sensitive=false,以便通过“/health”快速检查DataSource。您还可以使用“/beans”、“/env”和“/autoconfig”端点来查看自动配置中发生了什么以及原因。

注意:在 Cloud Foundry 中运行或在本地 classpath 中包含 auto-reconfiguration JAR 都会激活“cloud”配置文件(原因相同)。VCAP_*环境变量是触发 Spring Cloud 和/或 auto-reconfiguration JAR 创建 bean 的东西。

注意:VCAP_SERVICES中的 URL 实际上不是“jdbc” scheme,而这对于 JDBC 连接应该是强制性的。然而,这是 Cloud Foundry 通常呈现它的格式,因为它对几乎所有非 Java 语言都适用。Spring Cloud Connectors 或 buildpack 自动重新配置(如果它们创建DataSource)会为您将其翻译成jdbc:* URL。

注意:MySQL URL 也包含用户凭据和数据库名,这些凭据和数据库名对于示例源代码中的docker-compose.yml创建的 Docker 容器是有效的。如果您有具有不同凭据的本地 MySQL 服务器,可以替换它们。

提示:如果您使用本地 MySQL 服务器并想验证是否已连接,可以使用 Spring Boot Actuator 中的“/health”端点(已包含在示例代码中)。或者,您可以在 classpath 的根目录下创建一个schema-mysql.sql文件,并在其中放入一个简单的保持连接查询(例如,SELECT 1)。Spring Boot 会在启动时运行它,所以如果应用程序成功启动,说明您已正确配置数据库。

默认情况下,auto-reconfiguration JAR 始终位于 Cloud Foundry 的 classpath 中,但如果找到org.springframework.cloud.CloudFactory bean(如果CloudAutoConfiguration处于活动状态,Spring Boot 会提供此 bean),它就会停止创建任何DataSource。因此,在 Spring Boot 应用程序中,如果 Connectors 也存在,将其添加到 classpath 的最终效果只是激活“cloud”配置文件。您可以在启动时的应用程序日志中看到它决定跳过自动重新配置的决策。

015-04-14 15:11:11.765  INFO 12727 --- [           main] urceCloudServiceBeanFactoryPostProcessor : Skipping auto-reconfiguring beans of type javax.sql.DataSource
2015-04-14 15:11:57.650  INFO 12727 --- [           main] ongoCloudServiceBeanFactoryPostProcessor : Skipping auto-reconfiguring beans of type org.springframework.data.mongodb.MongoDbFactory
2015-04-14 15:11:57.650  INFO 12727 --- [           main] bbitCloudServiceBeanFactoryPostProcessor : Skipping auto-reconfiguring beans of type org.springframework.amqp.rabbit.connection.ConnectionFactory
2015-04-14 15:11:57.651  INFO 12727 --- [           main] edisCloudServiceBeanFactoryPostProcessor : Skipping auto-reconfiguring beans of type org.springframework.data.redis.connection.RedisConnectionFactory
...
etc.

创建自己的 DataSource

上一节介绍了各种库中大多数重要的自动配置功能。如果你想自己控制,可以从创建自己的 DataSource 实例开始。例如,你可以使用 DataSourceBuilder 来做到这一点,这是一个便利类,作为 Spring Boot 的一部分(它根据类路径选择实现)。

@SpringBootApplication
public class CloudApplication {
	
	@Bean
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}
	
	...

}

正如我们所定义的,这个DataSource是无用的,因为它没有连接 URL 或任何凭据,但这很容易修复。让我们在 Cloud Foundry 中运行此应用程序:使用VCAP_*环境变量和 auto-reconfiguration JAR,但 classpath 中没有 Spring Cloud Connectors,也没有显式的“cloud”配置文件。buildpack 激活了“cloud”配置文件,创建了一个DataSource并将其绑定到VCAP_SERVICES。正如之前简要描述的那样,它会完全移除您的DataSource,并将其替换为一个手动注册的单例(这不会显示在 Spring Boot 的“/beans”端点中)。

现在将 Spring Cloud Connectors 重新添加到应用程序的 classpath 中,看看再次运行时会发生什么。它实际上在启动时失败了!发生了什么?@ServiceScan(来自 Connectors)会查找绑定的服务,并为其创建 bean 定义。这有点像 buildpack,但不同之处在于它不会尝试替换相同类型的任何现有 bean 定义。所以您会得到一个 autowiring 错误,因为存在 2 个DataSource,并且无法选择一个注入到应用程序需要它的各个地方。

要解决这个问题,我们将不得不控制 Cloud Connectors(或者干脆不用它们)。

使用 CloudFactory 创建 DataSource

你可以通过创建一个 Cloud 实例作为 @Bean 来禁用 Spring Boot 自动配置以及 Java 构建包自动重新配置。

@Bean
public Cloud cloud() {
  return new CloudFactory().getCloud();
}

@Bean
@ConfigurationProperties(DataSourceProperties.PREFIX)
public DataSource dataSource() {
  return cloud().getSingletonServiceConnector(DataSource.class, null);
}

优点:Spring Boot 中 Connectors 的自动配置停止了,所以只有一个DataSource。可以通过spring.datasource.*属性(例如在application.properties中)进行调整,遵循Spring Boot 用户指南

缺点:如果没有VCAP_*环境变量(或某些其他云平台),它就无法工作。它还依赖于用户记住将Cloud创建为@Bean以禁用自动配置。

总结:我们仍然没有达到舒适的状态(一个没有对环境变量进行复杂调整就无法运行的应用程序在实践中用处不大)。

双环境运行:本地使用 H2,云端使用 MySQL

Spring Cloud Connectors 有一个本地配置文件选项,所以您不必在真正的云平台中才能使用它们,但这设置起来很麻烦,尽管它是样板代码,而且当您处于真正的云平台时,您还必须以某种方式将其关闭。最后一点才是真正重要的,因为最终您需要一个本地文件才能在本地运行,但这仅在本地运行,而且不能随应用程序代码一起打包(例如,这违反了十二因素指南)。

因此,要继续我们的显式 @Bean 定义,最好还是遵循主流的 Spring 和 Spring Boot 特性,例如使用“cloud”配置文件来保护 DataSource 的显式创建。

@Configuration
@Profile("cloud")
public class DataSourceConfiguration {

  @Bean
  public Cloud cloud() {
    return new CloudFactory().getCloud();
  }

  @Bean
  @ConfigurationProperties(DataSourceProperties.PREFIX)
  public DataSource dataSource() {
    return cloud().getSingletonServiceConnector(DataSource.class, null);
  }

}

有了这个设置,我们有了一个可以在本地和 Cloud Foundry 中平稳运行的解决方案。在本地,Spring Boot 将使用 H2 嵌入式数据库创建一个DataSource。在 Cloud Foundry 中,它将绑定到类型为DataSource的单例服务,并关闭来自 Spring Boot 的自动配置。它还有一个好处,就是可以与 Spring Cloud Connectors 支持的任何平台一起工作,所以相同的代码可以在 Heroku 和 Cloud Foundry 等平台上运行。由于@ConfigurationProperties,您可以将附加配置绑定到DataSource,以便在生产环境中根据需要调整连接池属性之类的东西。

注意:我们一直使用 MySQL 作为数据库服务器的示例,但实际上 PostgreSQL 至少是一个同样有吸引力(如果不是更具吸引力)的选择。例如,与本地 H2 配对时,您可以将 H2 设置为“Postgres 兼容”模式,并在两个环境中使用相同的 SQL。

手动创建本地和云端 DataSource

如果你喜欢创建 DataSource bean,并且想在本地和云端都这样做,你可以使用 2 个配置文件(例如,“cloud”和“local”)。但这样你就必须找到一种方法在不在云端时默认激活“local”配置文件。Spring 中已经内置了做到这一点的方法,因为总是有一个名为“default”的默认配置文件(默认情况下)。所以这样做应该可行:

@Configuration
@Profile("default") // or "!cloud"
public class LocalDataSourceConfiguration {
	
	@Bean
    @ConfigurationProperties(DataSourceProperties.PREFIX)
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

}

@Configuration
@Profile("cloud")
public class CloudDataSourceConfiguration {

  @Bean
  public Cloud cloud() {
    return new CloudFactory().getCloud();
  }

  @Bean
  @ConfigurationProperties(DataSourceProperties.PREFIX)
  public DataSource dataSource() {
    return cloud().getSingletonServiceConnector(DataSource.class, null);
  }

}

在这个简单的示例中,“default”DataSource实际上与自动配置的DataSource相同,所以除非您需要自定义(例如,创建 Spring Boot 不支持的特定具体类型的DataSource),否则您不会这样做。您可能会觉得事情变得有点复杂了,但实际上 Spring Boot 并没有使其更难,我们只是在处理需要在两个环境中控制DataSource构建所带来的后果。

在本地使用非嵌入式数据库

如果您不想在本地使用 H2 或任何内存数据库,那么您无法避免配置它(Spring Boot 可以从 URL 中猜测很多,但至少需要 URL)。所以至少您需要设置一些spring.datasource.*属性(例如 URL)。这并不难做,您可以使用附加的配置文件轻松地在不同环境中设置不同的值,但一旦您这样做,当您进入云端时,您就需要关闭默认值。为此,您可以将spring.datasource.*属性定义在特定于“default”配置文件的文件中(或 YAML 文档中),例如application-default.properties,这些属性在“cloud”配置文件中将不会被使用。

纯声明式方法

如果你不喜欢编写 Java 代码,或者不想使用 Spring Cloud Connectors,你可能想尝试使用 Spring Boot 自动配置和外部属性(或 YAML)文件来处理所有事情。例如,如果在类路径上找到了正确的依赖,Spring Boot 会为你创建一个 DataSource,并且可以通过 application.properties 完全控制它,包括在生产环境中需要的所有 DataSource 的细粒度功能(如连接池大小和验证查询)。因此,你只需要一种方法从环境中发现服务的地址和凭据。构建包会将 Cloud Foundry 的 VCAP_* 环境变量转换为 Spring Environment 中可用的属性源。因此,例如,一个 DataSource 配置可能看起来像这样:

spring.datasource.url: ${cloud.services.mysql.connection.jdbcurl:jdbc:h2:mem:testdb}
spring.datasource.username: ${cloud.services.mysql.connection.username:sa}
spring.datasource.password: ${cloud.services.mysql.connection.password:}
spring.datasource.testOnBorrow: true

属性名中的“mysql”部分是 Cloud Foundry 中的服务名称(所以它由用户设置)。当然,同样的模式适用于各种服务,不仅仅是 JDBC DataSource。一般来说,使用外部配置,特别是@ConfigurationProperties是一个好的做法,因为它们提供了最大的灵活性,例如在运行时通过系统属性或环境变量进行覆盖。

注意:Spring Boot 提供了类似的功能,它提供的是 vcap.services.* 而不是 cloud.services.*,所以实际上你有不止一种方法可以做到这一点。但是,JDBC URL 的名称略有不同,所以要注意“jdbcurl”与“jdbcUrl”。示例:

spring.datasource.url: ${vcap.services.mysql.credentials.jdbcUrl:jdbc:h2:mem:testdb}
spring.datasource.username: ${vcap.services.mysql.credentials.username:sa}
spring.datasource.password: ${vcap.services.mysql.credentials.password:}
spring.datasource.testOnBorrow: true

通常,非 JDBC 服务可以使用相应的vcap.services.*credentials.url正常工作。

这种方法的一个局限性在于,如果应用程序需要配置不是 Spring Boot 开箱即用提供的 bean(例如,如果您需要 2 个DataSource),则无法应用此方法,在这种情况下您仍然必须编写 Java 代码,并且可以选择是否使用属性文件对其进行参数化。

但是,在你亲自尝试之前,请注意,除非你同时禁用构建包自动重新配置(以及类路径上的 Spring Cloud Connectors),否则它实际上不起作用。如果你不这样做,它们会为你创建一个新的 DataSource,而 Spring Boot 无法将其绑定到你的属性文件。因此,即使对于这种声明式方法,你也需要一个环境变量 JBP_CONFIG_SPRING_AUTO_RECONFIGURATION='{ enabled: false }' 或为一个 Cloud bean 定义一个显式的 @Bean

@Configuration
@Profile("cloud")
public class CloudDataSourceConfiguration {

  @Bean
  public Cloud cloud() {
    return new CloudFactory().getCloud();
  }

}

这仅仅是为了关闭 buildpack 自动重新配置(以及 Spring Boot 自动配置,尽管后者可以通过属性文件条目禁用)。

注意:如果您使用JBP_CONFIG_SPRING_AUTO_RECONFIGURATION来关闭 buildpack 自动重新配置,您也会关闭“cloud”配置文件。当然,很容易重新添加它。

混合声明式和显式 Bean 定义

你也可以混合使用这两种方法:声明一个单独的 @Bean 定义,这样你就可以控制对象的构造,但使用 @ConfigurationProperties 将额外的配置绑定到它(并在本地和 Cloud Foundry 中做同样的事情)。示例:

@Configuration
public class LocalDataSourceConfiguration {
	
	@Bean
    @ConfigurationProperties(DataSourceProperties.PREFIX)
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

}

(其中DataSourceBuilder将被替换为您用例所需的任何复杂逻辑)。application.properties将与上面相同,包含您生产环境设置所需的任何附加属性。

第三种方式:发现凭据并手动绑定

另一种有利于平台和环境独立性的方法是为 Spring Boot 用于绑定其自动配置的连接器的 @ConfigurationProperties bean 声明显式 bean 定义。例如,要为 DataSource 设置默认值,你可以声明一个类型为 DataSourceProperties@Bean

@Bean
@Primary
public DataSourceProperties dataSourceProperties() {
    DataSourceProperties properties = new DataSourceProperties();
    properties.setInitialize(false);
    return properties;
}

这设置了“initialize”标志的默认值,并允许从 application.properties(或其他外部属性)绑定其他属性。将此与 Spring Cloud Connectors 结合使用,你可以在检测到云服务时控制凭据的绑定。


@Autowired(required="false")
Cloud cloud;

@Bean
@Primary
public DataSourceProperties dataSourceProperties() {
    DataSourceProperties properties = new DataSourceProperties();
    properties.setInitialize(false);
    if (cloud != null) {
      List<ServiceInfo> infos = cloud.getServiceInfos(RelationalServiceInfo.class);
      if (infos.size()==1) {
        RelationalServiceInfo info = (RelationalServiceInfo) infos.get(0);
        properties.setUrl(info.getJdbcUrl());
        properties.setUsername(info.getUserName());
        properties.setPassword(info.getPassword());
      }
    }
    return properties;
}

并且您仍然需要在“cloud”配置文件中定义Cloud bean。对于这个简单的用例来说,代码量相当大,而且很不必要,但如果您有更复杂的绑定,或者需要实现一些逻辑来在运行时选择DataSource,可能会很方便。

Spring Boot 为您可能常用到的其他中间件提供了类似的*Properties bean(例如,RabbitPropertiesRedisPropertiesMongoProperties)。一个标记为@Primary的此类 bean 实例足以重置自动配置连接器的默认值。

部署到多个云平台

到目前为止,我们一直专注于 Cloud Foundry 作为部署应用程序的唯一云平台。Spring Cloud Connectors 的一个优点是它支持其他平台,无论是开箱即用还是作为扩展点。spring-boot-starter-cloud-connectors甚至包含了 Heroku 支持。如果您什么都不做,仅依赖自动配置(懒惰程序员的方法),那么您的应用程序将可以部署到所有 classpath 中有连接器的云端(即如果您使用 starter,则包括 Cloud Foundry 和 Heroku)。如果您采用显式@Bean方法,则需要确保在非 Cloud Foundry 平台中激活“cloud”配置文件,例如通过环境变量。如果您使用纯声明式方法(或任何涉及属性文件的组合方法),则需要激活“cloud”配置文件,并且可能还需要一个特定于您平台的其他配置文件,以便正确的属性文件最终在运行时进入Environment

自动配置和提供行为总结

  • Spring Boot 会在 classpath 中找到所有合适的内容时提供DataSource(也包括 RabbitMQ 或 Redis ConnectionFactory、Mongo 等)。使用“spring-boot-starter-*”依赖项足以激活此行为。

  • 如果在类路径上找到 Spring Cloud Connectors,Spring Boot 还会提供一个可自动装配的 CloudFactory(但只有在找到类型为 Cloud@Bean 时才会关闭)。

  • Spring Boot 中的 CloudAutoConfiguration 实际上也会为你的应用程序添加一个 @CloudScan,如果你需要创建自己的 DataSource(或类似对象),你就会想关闭它。

  • Cloud Foundry Java 构建包检测到 Spring Boot 应用程序并激活“cloud”配置文件,除非它已经激活。如果你想在本地尝试,添加构建包自动重新配置 JAR 也会做同样的事情。你可以通过设置 JBP_CONFIG_SPRING_AUTO_RECONFIGURATION='{ enabled: false }' 来禁用它。

  • 通过自动重新配置 JAR,构建包也会启动并创建一个 DataSource(RabbitMQ、Redis、Mongo 等也一样),如果它没有找到 CloudFactory bean 或 Cloud bean(以及其他)。因此,在 Spring Boot 应用程序中包含 Spring Cloud Connectors 会关闭“自动重新配置”行为的这一部分(bean 创建)。

  • 关闭 Spring Boot 的 CloudAutoConfiguration 很简单,但如果你这样做,如果你不想让构建包自动重新配置生效,就必须记住同时关闭它。有两种方法可以做到这一点。一种是环境变量:JBP_CONFIG_SPRING_AUTO_RECONFIGURATION='{ enabled: false }'。另一种是定义一个 bean 定义(例如,类型可以是 CloudCloudFactory)。

  • Spring Boot 会将 application.properties(以及其他外部属性来源)绑定到 @ConfigurationProperties bean,包括但不限于它自动配置的那些。你可以使用此功能调整连接池属性和需要在生产环境中不同的其他设置。

一般建议和结论

在这篇短文中,我们看到了相当多的选项和自动配置,而且我们实际上只使用了三个库(Spring Boot、Spring Cloud Connectors 和 Cloud Foundry 构建包自动重新配置 JAR)和一个平台(Cloud Foundry),还没算上本地部署。构建包的功能实际上只对非常简单的应用程序有用,因为在生产环境中没有灵活性来调整连接。话虽如此,在原型开发时能够这样做是很不错的。如果你想实现同一段代码在本地和云端都能部署的目标,同时仍能在生产环境中进行必要的调整,那么只有三种主要方法:

  1. 使用 Spring Cloud Connectors 显式创建 DataSource 和其他中间件连接,并使用 @Profile("cloud") 保护这些 @Bean。这种方法总是可行,但会导致代码量比许多应用程序所需的更多。

  2. 使用 Spring Boot 默认自动配置,并使用 application.properties(或 YAML 文件)声明云绑定。要充分利用这一点,你必须显式关闭构建包自动重新配置。

  3. 使用 Spring Cloud Connectors 发现凭据,并在存在时将其绑定到 Spring Boot 的 @ConfigurationProperties 作为默认值。

这三种方法实际上并不冲突,可以使用 @ConfigurationProperties 混合使用,以提供特定配置文件的默认配置覆盖(例如,在生产环境中以不同方式设置连接池)。如果你有一个相对简单的 Spring Boot 应用程序,选择哪种方法可能只是个人喜好。如果你有一个非 Spring Boot 应用程序,那么显式的 @Bean 方法会胜出,如果你计划将应用程序部署到多个云平台(例如 Heroku 和 Cloud Foundry),它也可能胜出。

注意:这篇博文是一段探索之旅(谁知道有这么多需要学习呢?)。感谢所有帮助审查和评论的人,特别是 Scott Frederick,他发现了草稿中的大多数错误,并且总是愿意花时间查看新的修订版本。

获取 Spring 通讯

订阅 Spring 通讯,保持联系

订阅

领先一步

VMware 提供培训和认证,助你快速提升。

了解更多

获取支持

Tanzu Spring 通过一次简单的订阅即可为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部