1. Overview
1.概述
In this tutorial, we’ll learn about the ConnectionDetails interface introduced in Spring Boot 3.1 to externalize the connection properties. Spring Boot provides out-of-the-box abstractions to integrate with remote services like relational databases, NoSQL databases, messaging services, etc.
在本教程中,我们将学习 Spring Boot 3.1 中引入的 ConnectionDetails 接口,以便将连接属性外部化。 Spring Boot 提供了开箱即用的抽象,以便与关系数据库、NoSQL 数据库、消息服务等远程服务集成。
Traditionally, the application.properties file is used for storing the connection details of the remote services. Hence, it becomes difficult to externalize these properties to an external service like AWS Secret Manager, Hashicorp Vault, etc.
传统上,application.properties 文件用于存储远程服务的连接详细信息。因此,很难将这些属性外部化到外部服务,如 AWS Secret Manager, Hashicorp Vault 等。
To address this concern, Spring Boot has introduced ConnectionDetails. This interface is empty and acts like a tag. Spring offers subinterfaces of this, such as JdbcConnectionDetails, CassandraConnectionDetails, KafkaConnectionDetails, and more. They can be implemented and specified in the Spring configuration classes as beans. After this, Spring relies on these configuration beans to dynamically retrieve the connection properties, instead of the static application.properties file.
为了解决这一问题,Spring Boot 引入了 ConnectionDetails。这个接口是空的,就像一个标签。Spring 提供了该接口的子接口,例如 JdbcConnectionDetails、CassandraConnectionDetails、KafkaConnectionDetails 等。它们可以作为 Bean 在 Spring 配置类中实现和指定。之后,Spring 将依靠这些配置 Bean 来动态检索连接属性,而不是静态的 application.properties 文件。
We’d begin with an introduction to a use case and then move on to its implementation.
我们首先会介绍一个用例,然后开始实施。
2. Use Case Description
2.用例说明
Let’s imagine a multinational bank called the Bank of Malgudi. It operates numerous applications running on Spring Boot. These applications connect to various remote services. Presently, the connection details of these remote services are stored in the application.properties file.
让我们设想一家名为马尔古迪银行的跨国银行。它运行着大量基于 Spring Boot 的应用程序。这些应用程序连接到各种远程服务。目前,这些远程服务的连接详细信息存储在 application.properties 文件中。
After a recent review, the Compliance Body in the Bank raised several concerns about the security of these properties. A few of their requests pertaining to these concerns are:
在最近一次审查之后,银行的合规机构对这些财产的安全提出了一些关切。他们就这些问题提出了以下要求:
- Encrypt all the secrets
- Periodically rotate the secrets
- No exchange of secrets in emails
3. Proposed Solution and Design
3.建议的解决方案和设计
The application owners in the Bank of Malgudi brainstormed on the above concerns and finally came up with a solution. They proposed moving all the secrets to Hashicorp Vault.
马尔古迪银行的应用程序所有者就上述问题集思广益,最终提出了一个解决方案。他们提议将所有机密转移到 Hashicorp Vault。
As a result, all the Spring Boot applications have to read the secrets from the Vault. Following is the proposed high-level design:
因此,所有 Spring Boot 应用程序都必须从 Vault 中读取机密。以下是建议的高级设计:
Now, the Spring Boot application has to call the Vault service with a secret key to retrieve the secret. Then with the retrieved secret, it can call the remote service to get the connection object for further operations.
现在,Spring Boot 应用程序必须使用密钥调用 Vault 服务来获取密钥。然后,它就可以使用检索到的密钥调用远程服务,获取连接对象以进行进一步操作。
Consequently, applications will rely on the Vault to take care of storing the secrets safely. Vault would periodically rotate the secrets as per the organization’s policy. In case the applications cache the secrets, they also have to reload them.
因此,应用程序将依靠保管库来安全存储机密。Vault 会根据组织政策定期轮换机密。 如果应用程序缓存了机密,也必须重新加载。
4. Implementation With ConnectionDetails
4.使用 ConnectionDetails 实现
With the ConnectionDetails interface, Spring Boot applications can discover the connection details themselves without any manual intervention. Having said this, it’s important to note that ConnectionDetails takes precedence over the application.properties file. However, there are still some non-connection properties like JDBC connection pool size, that can still be configured through the application.properties file.
借助 ConnectionDetails 接口,Spring Boot 应用程序无需任何手动干预即可自行发现连接详细信息。尽管如此,重要的是要注意 ConnectionDetails 优先于 application.properties 文件。不过,仍有一些非连接属性(如 JDBC 连接池大小)可以通过 application.properties 文件进行配置。
In the upcoming section, we’ll see various ConnectionDetails implementation classes in action by leveraging the Spring Boot docker compose feature.
在接下来的章节中,我们将看到利用 Spring Boot docker compose 功能实现的各种 ConnectionDetails 实现类。
4.1. Externalize JDBC Connection Details
4.1.外部化 JDBC 连接详细信息
Here we’ll take the example of the Spring Boot application’s integration with Postgres Database. Let’s begin with the class diagram:
下面我们以 Spring Boot 应用程序与 Postgres 数据库的集成为例。让我们从类图开始:
In the above class diagram, the JdbcConnectionDetails interface is from the Spring Boot framework. The PostgresConnectionDetails class implements the methods of the interface to fetch the details from the Vault:
在上述类图中,JdbcConnectionDetails 接口来自 Spring Boot 框架。PostgresConnectionDetails 类实现了该接口的方法,以便从 Vault 获取详细信息:
public class PostgresConnectionDetails implements JdbcConnectionDetails {
@Override
public String getUsername() {
return VaultAdapter.getSecret("postgres_user_key");
}
@Override
public String getPassword() {
return VaultAdapter.getSecret("postgres_secret_key");
}
@Override
public String getJdbcUrl() {
return VaultAdapter.getSecret("postgres_jdbc_url");
}
}
As shown below, JdbcConnectionDetailsConfiguration is the configuration class in the application:
如下所示,JdbcConnectionDetailsConfiguration 是应用程序中的配置类:
@Configuration(proxyBeanMethods = false)
public class JdbcConnectionDetailsConfiguration {
@Bean
@Primary
public JdbcConnectionDetails getPostgresConnection() {
return new PostgresConnectionDetails();
}
}
Interestingly Spring Boot automatically discovers it during the application boot-up process and gets the JdbcConnectionDetails bean. As explained earlier, the bean holds the logic to retrieve the Postgres database connection details from the Vault.
有趣的是,Spring Boot 会在应用程序启动过程中自动发现它,并获取 JdbcConnectionDetails bean。如前所述,该 bean 包含从 Vault 检索 Postgres 数据库连接详细信息的逻辑。
Since we’re using Docker Compose to start the Postgres database container, Spring Boot automatically creates a ConnectionDetails bean holding the necessary connection details. Hence we’re using @Primary annotation to give the JdbcConectionDetails bean precedence over it.
由于我们使用 Docker Compose 来启动 Postgres 数据库容器,因此 Spring Boot 会自动创建一个 ConnectionDetailsbean 来保存必要的连接详细信息。因此,我们使用 @Primary 注解使 JdbcConectionDetails bean 优先于它。
Let’s take a look at how it works:
让我们来看看它是如何工作的:
@Test
public void givenSecretVault_whenIntegrateWithPostgres_thenConnectionSuccessful() {
String sql = "select current_date;";
Date date = jdbcTemplate.queryForObject(sql, Date.class);
assertEquals(LocalDate.now().toString(), date.toString());
}
As expected, the application connects to the database and fetches the result successfully.
不出所料,应用程序连接到数据库并成功获取结果。
4.2. Externalize Rabbit MQ Connection Details
4.2.外部化 Rabbit MQ 连接详细信息
Similar to JdbcConnectionDetails, Spring Boot has provided the interface RabbitConnectionDetails to integrate with RabbitMQ Server. Let’s see how to use this interface to externalize the Spring Boot properties for connecting to RabbitMQ Server:
与 JdbcConnectionDetails 类似,Spring Boot 提供了接口 RabbitConnectionDetails 以集成 RabbitMQ Server 。让我们来看看如何使用此接口将 Spring Boot 属性外部化,以便连接到 RabbitMQ Server:
First, as per the contract let’s implement the RabbitConnectionDetails interface to fetch the connection properties from the Vault:
首先,让我们根据合同实现 RabbitConnectionDetails 接口,以便从 Vault 获取连接属性:
public class RabbitMQConnectionDetails implements RabbitConnectionDetails {
@Override
public String getUsername() {
return VaultAdapter.getSecret("rabbitmq_username");
}
@Override
public String getPassword() {
return VaultAdapter.getSecret("rabbitmq_password");
}
@Override
public String getVirtualHost() {
return "/";
}
@Override
public List<Address> getAddresses() {
return List.of(this.getFirstAddress());
}
@Override
public Address getFirstAddress() {
return new Address(VaultAdapter.getSecret("rabbitmq_host"),
Integer.valueOf(VaultAdapter.getSecret("rabbitmq_port")));
}
}
Next, we’ll define the above bean RabbitMQConnectionDetails in the RabbitMQConnectionDetailsConfiguration class:
接下来,我们将在 RabbitMQConnectionDetailsConfiguration 类中定义上述 Bean RabbitMQConnectionDetails :
@Configuration(proxyBeanMethods = false)
public class RabbitMQConnectionDetailsConfiguration {
@Primary
@Bean
public RabbitConnectionDetails getRabbitmqConnection() {
return new RabbitMQConnectionDetails();
}
}
Finally, let’s see if that works:
最后,让我们看看这是否有效:
@Test
public void givenSecretVault_whenPublishMessageToRabbitmq_thenSuccess() {
final String MSG = "this is a test message";
this.rabbitTemplate.convertAndSend(queueName, MSG);
assertEquals(MSG, this.rabbitTemplate.receiveAndConvert(queueName));
}
The above method sends a message to a queue in RabbitMQ and then reads it. The object rabbitTemplate is auto-configured by Spring Boot by referring to the connection details in the RabbitMQConnectionDetails bean. We injected the rabbitTemplate object into the test class and then used it in the above test method.
上述方法向 RabbitMQ 中的队列发送消息,然后读取消息。Spring Boot 通过引用 RabbitMQConnectionDetails bean 中的连接详细信息自动配置了对象 rabbitTemplate 。我们将 rabbitTemplate 对象注入测试类,然后将其用于上述测试方法。
4.3. Externalize Redis Connection Details
4.3.外部化 Redis 连接详情
Now let’s move on to the Spring ConnectionDetails abstraction on Redis. First, we’ll start with the class diagram:
现在,让我们来看看 Redis 上的 Spring ConnectionDetails 抽象。 首先,我们从类图开始:
Let’s take a look at RedisCacheConnectionDetails, which has externalized the connection properties of Redis by implementing RedisConnectionDetails:
让我们来看看 RedisCacheConnectionDetails, 它通过实现 RedisConnectionDetails 来外部化 Redis 的连接属性:
public class RedisCacheConnectionDetails implements RedisConnectionDetails {
@Override
public String getPassword() {
return VaultAdapter.getSecret("redis_password");
}
@Override
public Standalone getStandalone() {
return new Standalone() {
@Override
public String getHost() {
return VaultAdapter.getSecret("redis_host");
}
@Override
public int getPort() {
return Integer.valueOf(VaultAdapter.getSecret("redis_port"));
}
};
}
}
As shown below, the configuration class RedisConnectionDetailsConfiguration returns the RedisConnectionDetails bean:
如下所示,配置类 RedisConnectionDetailsConfiguration 返回 RedisConnectionDetails Bean:
@Configuration(proxyBeanMethods = false)
@Profile("redis")
public class RedisConnectionDetailsConfiguration {
@Bean
@Primary
public RedisConnectionDetails getRedisCacheConnection() {
return new RedisCacheConnectionDetails();
}
}
Finally, let’s see if we can integrate with Redis:
最后,让我们看看能否与 Redis 集成:
@Test
public void giveSecretVault_whenStoreInRedisCache_thenSuccess() {
redisTemplate.opsForValue().set("City", "New York");
assertEquals("New York", redisTemplate.opsForValue().get("City"));
}
First, the Spring framework successfully injects redisTemplate into the test class. Then it’s used for adding a key-value pair into the cache. Finally, we retrieve the value as well.
首先,Spring 框架成功地将 redisTemplate 注入测试类。然后,它被用于将键值对添加到缓存中。最后,我们还检索了值。
4.4. Externalize MongoDB Connection Details
4.4.外部化 MongoDB 连接详情
Just like before, let’s begin with the usual class diagram:
和以前一样,让我们从常见的类图开始:
Let’s take a look at the implementation of the MongoDBConnectionDetails class above:
让我们来看看上述 MongoDBConnectionDetails 类的实现:
public class MongoDBConnectionDetails implements MongoConnectionDetails {
@Override
public ConnectionString getConnectionString() {
return new ConnectionString(VaultAdapter.getSecret("mongo_connection_string"));
}
}
Just like the class diagram, we have implemented the method getConnectionString() of MongoConnectionDetails interface. The method getConnectionString() retrieves the connection string from the Vault.
正如类图所示,我们实现了 MongoConnectionDetails 接口的方法 getConnectionString() 。方法 getConnectionString() 从 Vault 中检索连接字符串。
We can now take a look at how the MongoDBConnectionDetailsConfiguration class creates the MongoConnectionDetails bean:
现在,我们可以看看 MongoDBConnectionDetailsConfiguration 类是如何创建 MongoConnectionDetails Bean 的:
@Configuration(proxyBeanMethods = false)
public class MongoDBConnectionDetailsConfiguration {
@Bean
@Primary
public MongoConnectionDetails getMongoConnectionDetails() {
return new MongoDBConnectionDetails();
}
}
Let’s see if our efforts result in successful integration with MongoDB Server:
让我们看看我们的努力是否能成功集成MongoDB 服务器:
@Test
public void givenSecretVault_whenExecuteQueryOnMongoDB_ReturnResult() {
mongoTemplate.insert("{\"msg\":\"My First Entry in MongoDB\"}", "myDemoCollection");
String result = mongoTemplate.find(new Query(), String.class, "myDemoCollection").get(0);
JSONObject jsonObject = new JSONObject(result);
result = jsonObject.get("msg").toString();
assertEquals("My First Entry in MongoDB", result);
}
So, as we see above, the method inserts data into MongoDB and then retrieves it successfully. This is possible because Spring Boot creates the mongoTemplate bean with the help of MongoConnectionDetails bean defined in MongoDBConnectionDetailsConfiguration.
因此,正如我们在上文所看到的,该方法将数据插入 MongoDB,然后成功检索数据。这是因为 Spring Boot 借助 MongoDBConnectionDetailsConfiguration 中定义的 MongoConnectionDetails Bean 创建了 MongoTemplate Bean。
4.5. Externalize R2dbc Connection Details
4.5.外部化 R2dbc 连接详情
Spring Boot has also provided ConnectionDetails abstraction for Reactive Relational Database Connection programming with the help of R2dbcConnectionDetails. Let’s take a look at the following class diagram to externalize the connection details:
Spring Boot 还借助 R2dbcConnectionDetails 为 Reactive 关系数据库连接编程提供了 ConnectionDetails 抽象。让我们来看看下面的类图,以便将连接详细信息外部化:
First, let’s implement R2dbcPostgresConnectionDetails:
首先,让我们来实现 R2dbcPostgresConnectionDetails:
public class R2dbcPostgresConnectionDetails implements R2dbcConnectionDetails {
@Override
public ConnectionFactoryOptions getConnectionFactoryOptions() {
ConnectionFactoryOptions options = ConnectionFactoryOptions.builder()
.option(ConnectionFactoryOptions.DRIVER, "postgresql")
.option(ConnectionFactoryOptions.HOST, VaultAdapter.getSecret("r2dbc_postgres_host"))
.option(ConnectionFactoryOptions.PORT, Integer.valueOf(VaultAdapter.getSecret("r2dbc_postgres_port")))
.option(ConnectionFactoryOptions.USER, VaultAdapter.getSecret("r2dbc_postgres_user"))
.option(ConnectionFactoryOptions.PASSWORD, VaultAdapter.getSecret("r2dbc_postgres_secret"))
.option(ConnectionFactoryOptions.DATABASE, VaultAdapter.getSecret("r2dbc_postgres_database"))
.build();
return options;
}
}
Just like in earlier sections, here also we’ve used VaultAdapter to retrieve the connection details.
与前面的章节一样,这里我们也使用了 VaultAdapter 来检索连接详细信息。
Now, let’s implement the R2dbcPostgresConnectionDetailsConfiguration class to return R2dbcPostgresConnectionDetails as a Spring bean:
现在,让我们实现 R2dbcPostgresConnectionDetailsConfiguration 类,将 R2dbcPostgresConnectionDetails 作为 Spring Bean 返回:
@Configuration(proxyBeanMethods = false)
public class R2dbcPostgresConnectionDetailsConfiguration {
@Bean
@Primary
public R2dbcConnectionDetails getR2dbcPostgresConnectionDetails() {
return new R2dbcPostgresConnectionDetails();
}
}
Because of the above bean, the Spring Boot framework auto-configures R2dbcEntityTemplate. Finally, it can be autowired and used for running queries in a reactive manner:
由于上述 bean 的存在,Spring Boot 框架自动配置了 R2dbcEntityTemplate 。 最后,它可以自动接线并用于以反应方式运行查询:
@Test
public void givenSecretVault_whenQueryPostgresReactive_thenSuccess() {
String sql = "select * from information_schema.tables";
List<String> result = r2dbcEntityTemplate.getDatabaseClient().sql(sql).fetch().all()
.map(r -> {
return "hello " + r.get("table_name").toString();
}).collectList().block();
logger.info("count ------" + result.size());
}
4.6. Externalize Elasticsearch Connection Details
4.6.外部化 Elasticsearch 连接详细信息
For externalizing the connection details of the Elasticsearch service, Spring Boot offers the interface ElasticsearchConnectionDetails. Let’s begin by taking a look at the following class diagram:
为了将 Elasticsearch 服务的连接详细信息外部化,Spring Boot 提供了接口 ElasticsearchConnectionDetails 。 让我们先来看看下面的类图:
Just like before, we adopted the same pattern for retrieving the connection details. Now, we can move on to the implementation, starting with the CustomElasticsearchConnectionDetails class:
与之前一样,我们采用了相同的模式来检索连接详细信息。现在,我们可以从 CustomElasticsearchConnectionDetails 类开始着手实现:
public class CustomElasticsearchConnectionDetails implements ElasticsearchConnectionDetails {
@Override
public List<Node> getNodes() {
Node node1 = new Node(
VaultAdapter.getSecret("elastic_host"),
Integer.valueOf(VaultAdapter.getSecret("elastic_port1")),
Node.Protocol.HTTP
);
Node node2 = new Node(
VaultAdapter.getSecret("elastic_host"),
Integer.valueOf(VaultAdapter.getSecret("elastic_port2")),
Node.Protocol.HTTP
);
return List.of(node1, node2);
}
@Override
public String getUsername() {
return VaultAdapter.getSecret("elastic_user");
}
@Override
public String getPassword() {
return VaultAdapter.getSecret("elastic_secret");
}
}
The class sets the connection details by using the VaultAdapter.
该类通过使用 VaultAdapter 设置连接详情。
Let’s take a look at the configuration class that Spring Boot uses to discover the ElasticSearchConnectionDetails bean:
让我们来看看 Spring Boot 用来发现 ElasticSearchConnectionDetails bean 的配置类:
@Configuration(proxyBeanMethods = false)
@Profile("elastic")
public class CustomElasticsearchConnectionDetailsConfiguration {
@Bean
@Primary
public ElasticsearchConnectionDetails getCustomElasticConnectionDetails() {
return new CustomElasticsearchConnectionDetails();
}
}
Finally, it’s time to check how it works:
最后,是时候检查一下它是如何工作的了:
@Test
public void givenSecretVault_whenCreateIndexInElastic_thenSuccess() {
Boolean result = elasticsearchTemplate.indexOps(Person.class).create();
logger.info("index created:" + result);
assertTrue(result);
}
Interestingly, Spring Boot autoconfigures elasticsearchTemplate with the correct connection details into the test class. Then, it’s used for creating an index in Elasticsearch.
有趣的是,Spring Boot 会自动配置 elasticsearchTemplate 并将正确的连接详细信息添加到测试类中。然后,它将用于在 Elasticsearch 中创建索引。
4.7. Externalize Cassandra Connection Details
4.7.外部化 Cassandra 连接详情
As usual, the following is the class diagram of the proposed implementation:
与往常一样,下面是拟议实现的类图:
According to Spring Boot, we have to implement the methods of CassandraConnectionDetails interface as shown above. Let’s see the implementation of the class CustomCassandraConnectionDetails:
根据 Spring Boot,我们必须实现 CassandraConnectionDetails 接口的方法,如上所示。让我们来看看 CustomCassandraConnectionDetails 类的实现:</em
public class CustomCassandraConnectionDetails implements CassandraConnectionDetails {
@Override
public List<Node> getContactPoints() {
Node node = new Node(
VaultAdapter.getSecret("cassandra_host"),
Integer.parseInt(VaultAdapter.getSecret("cassandra_port"))
);
return List.of(node);
}
@Override
public String getUsername() {
return VaultAdapter.getSecret("cassandra_user");
}
@Override
public String getPassword() {
return VaultAdapter.getSecret("cassandra_secret");
}
@Override
public String getLocalDatacenter() {
return "datacenter-1";
}
}
Basically, we’re retrieving most of the sensitive connection details from the Vault.
基本上,我们正在从保险库中检索大部分敏感的连接细节。
Now, we can take a look at the configuration class which is responsible for creating the CustomCassandraConnectionDetails bean:
现在,我们可以看看负责创建 CustomCassandraConnectionDetails Bean 的配置类:
@Configuration(proxyBeanMethods = false)
public class CustomCassandraConnectionDetailsConfiguration {
@Bean
@Primary
public CassandraConnectionDetails getCustomCassandraConnectionDetails() {
return new CustomCassandraConnectionDetails();
}
}
Finally, let’s see if Spring Boot is able to auto-configure CassandraTemplate:
最后,让我们看看 Spring Boot 是否能够自动配置 CassandraTemplate :
@Test
public void givenSecretVaultVault_whenRunQuery_thenSuccess() {
Boolean result = cassandraTemplate.getCqlOperations()
.execute("CREATE KEYSPACE IF NOT EXISTS spring_cassandra"
+ " WITH replication = {'class':'SimpleStrategy', 'replication_factor':3}");
logger.info("the result -" + result);
assertTrue(result);
}
Using cassandraTemplate, the above method successfully creates a keyspace in the Cassandra database.
上述方法使用 cassandraTemplate 成功在 Cassandra 数据库中创建了一个键空间。
4.8. Externalize Neo4j Connection Details
4.8.外部化 Neo4j 连接详情
Spring Boot provides ConnectionDetails abstraction for Neo4j Database which is a popular graph database:
Spring Boot 为Neo4j 数据库提供了ConnectionDetails抽象,这是一种流行的图形数据库:
Moving on, let’s implement CustomNeo4jConnectionDetails:
接下来,让我们来实现 CustomNeo4jConnectionDetails:
public class CustomNeo4jConnectionDetails implements Neo4jConnectionDetails {
@Override
public URI getUri() {
try {
return new URI(VaultAdapter.getSecret("neo4j_uri"));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
@Override
public AuthToken getAuthToken() {
return AuthTokens.basic("neo4j", VaultAdapter.getSecret("neo4j_secret"));
}
}
Again, here also we’re reading the connection details from the Vault using VaultAdapter.
同样,在这里我们使用 VaultAdapter 从 Vault 读取连接详细信息。
Now, let’s implement the CustomNeo4jConnectionDetailsConfiguration:
现在,让我们来实现 CustomNeo4jConnectionDetailsConfiguration:</em
@Configuration(proxyBeanMethods = false)
public class CustomNeo4jConnectionDetailsConfiguration {
@Bean
@Primary
public Neo4jConnectionDetails getNeo4jConnectionDetails() {
return new CustomNeo4jConnectionDetails();
}
}
Spring Boot framework loads Neo4jConncetionDetails bean using the above configuration class.
Spring Boot 框架使用上述配置类加载 Neo4jConncetionDetails Bean。
Finally, it’s time to see if the following method successfully connects to Neo4j Database:
最后,看看下面的方法是否能成功连接 Neo4j 数据库:
@Test
public void giveSecretVault_whenRunQuery_thenSuccess() {
Person person = new Person();
person.setName("James");
person.setZipcode("751003");
Person data = neo4jTemplate.save(person);
assertEquals("James", data.getName());
}
Remarkably, Neo4jTemplate is autowired to the test class and it saves the data into the database.
值得注意的是,Neo4jTemplate 自动连接到测试类,并将数据保存到数据库中。
4.9. Externalize Kafka Connection Details
4.9.外部化 Kafka 连接详细信息
Kafka, being a popular and extremely powerful messaging broker, Spring Boot has provided integration libraries for it as well. KafkaConnectionDetails is the latest feature by Spring to support externalizing the connection properties. Hence, let’s see how we can use it with the help of the following class diagram:
Kafka 是一种流行且功能强大的消息传递代理,Spring Boot 也为其提供了集成库。KafkaConnectionDetails 是 Spring 支持连接属性外部化的最新功能。因此,让我们借助以下类图了解如何使用它:
The above design is mostly similar to the earlier designs discussed so far. Hence, we’ll straightaway jump to its implementation, starting with the CustomKafkaConnectionDetails class:
上述设计与迄今为止讨论过的早期设计基本相似。因此,我们将从 CustomKafkaConnectionDetails 类开始,直接跳转到其实现:
public class CustomKafkaConnectionDetails implements KafkaConnectionDetails {
@Override
public List<String> getBootstrapServers() {
return List.of(VaultAdapter.getSecret("kafka_servers"));
}
}
For a very basic setup of a Kafka single-node server, the above class just overrode the method getBootstrapServers() to read properties from the vault. For a more complex multinode setup, there are other methods that can be overridden.
对于非常基本的 Kafka 单节点服务器设置,上述类只需重载方法 getBootstrapServers() 以从 vault 中读取属性。对于更复杂的多节点设置,还可以重载其他方法。
We can now have a look at the CustomKafkaConnectionDetailsConfiguration class:
现在我们来看看 CustomKafkaConnectionDetailsConfiguration 类:
@Configuration(proxyBeanMethods = false)
public class CustomKafkaConnectionDetailsConfiguration {
@Bean
public KafkaConnectionDetails getKafkaConnectionDetails() {
return new CustomKafkaConnectionDetails();
}
}
The above method returns the KafkaConnectionDetails bean. And finally, that gets used by Spring to inject kafkaTemplate into the following method:
上述方法将返回 KafkaConnectionDetails Bean。最后,Spring 会使用该方法将 kafkaTemplate 注入到以下方法中:
@Test
public void givenSecretVault_whenPublishMsgToKafkaQueue_thenSuccess() {
assertDoesNotThrow(kafkaTemplate::getDefaultTopic);
}
4.10. Externalize Couchbase Connection Details
4.10.外部化 Couchbase 连接详情
Spring Boot has also provided the interface CouchbaseConnectionDetails for externalizing the connection properties of the Couchbase database. Let’s take a look at the following class diagram:
Spring Boot 还提供了接口CouchbaseConnectionDetails,用于外部化Couchbase数据库的连接属性。让我们来看看下面的类图:
We’ll first implement the CouchbaseConnectionDetails interface by overriding its methods to fetch the user, password, and connection string:
我们将首先实现 CouchbaseConnectionDetails 接口,重写其方法以获取用户、密码和连接字符串:
public class CustomCouchBaseConnectionDetails implements CouchbaseConnectionDetails {
@Override
public String getConnectionString() {
return VaultAdapter.getSecret("couch_connection_string");
}
@Override
public String getUsername() {
return VaultAdapter.getSecret("couch_user");
}
@Override
public String getPassword() {
return VaultAdapter.getSecret("couch_secret");
}
}
Then, we’ll create the above custom bean in the CustomCouchBaseConnectionDetails class:
然后,我们将在 CustomCouchBaseConnectionDetails 类中创建上述自定义 Bean:
@Configuration(proxyBeanMethods = false)
@Profile("couch")
public class CustomCouchBaseConnectionDetailsConfiguration {
@Bean
public CouchbaseConnectionDetails getCouchBaseConnectionDetails() {
return new CustomCouchBaseConnectionDetails();
}
}
Spring Boot, loads the above configuration class when the application boots up.
Spring Boot 会在应用程序启动时加载上述配置类。
Now, we can check the following method where we’re able to successfully connect with Couchbase server:
现在,我们可以检查下面的方法,是否能成功连接 Couchbase 服务器:
@Test
public void givenSecretVault_whenConnectWithCouch_thenSuccess() {
assertDoesNotThrow(cluster.ping()::version);
}
The Cluster class gets autowired in the method and then it’s used for integrating with the database.
Cluster 类将在该方法中自动连接,然后用于与数据库集成。
4.11. Externalize Zipkin Connection Details
4.11.外部化 Zipkin 连接细节
Finally, in this section, we’ll discuss the ZipkinConnectionDetails interface for externalizing the properties for connecting to Zipkin Server which is a popular distributed tracing system. Let’s begin with the following class diagram:
最后,在本节中,我们将讨论 ZipkinConnectionDetails 接口,该接口用于外部化连接到 Zipkin Server 的属性,Zipkin Server 是一种流行的分布式跟踪系统。让我们从下面的类图开始:
Using the above design in the class diagram, we’ll first implement CustomZipkinConnectionDetails:
使用类图中的上述设计,我们首先实现 CustomZipkinConnectionDetails :
public class CustomZipkinConnectionDetails implements ZipkinConnectionDetails {
@Override
public String getSpanEndpoint() {
return VaultAdapter.getSecret("zipkin_span_endpoint");
}
}
The method getSpanEndpoint() fetches the Zipkin API endpoint from the Vault using the VaultAdapter.
方法 getSpanEndpoint() 使用 VaultAdapter 从 Vault 获取 Zipkin API 端点。
Next, we’ll implement CustomZipkinConnectionDetailsConfiguration class:
接下来,我们将实现 CustomZipkinConnectionDetailsConfiguration 类:
@Configuration(proxyBeanMethods = false)
@Profile("zipkin")
public class CustomZipkinConnectionDetailsConfiguration {
@Bean
@Primary
public ZipkinConnectionDetails getZipkinConnectionDetails() {
return new CustomZipkinConnectionDetails();
}
}
As we can see, it returns the ZipkinConnectionDetails bean. During the application bootup, Spring Boot discovers the bean so that Zipkin libraries can push the trace information into Zipkin.
我们可以看到,它返回 ZipkinConnectionDetails Bean。在应用程序启动过程中,Spring Boot 会发现该 Bean,以便 Zipkin 库可以将跟踪信息推送到 Zipkin 中。
Let’s first run the application:
让我们先运行应用程序:
mvn spring-boot:run -P connection-details
-Dspring-boot.run.arguments="--spring.config.location=./target/classes/connectiondetails/application-zipkin.properties"
Before running the application, we must have Zipkin running on our local workstation.
在运行应用程序之前,我们必须在本地工作站上运行 Zipkin。
Then, we’ll run the following command to access a controller endpoint defined in ZipkinDemoController:
然后,我们将运行以下命令来访问 ZipkinDemoController 中定义的控制器端点:
curl http://localhost:8080/zipkin/test
Finally, we can check the trace on the Zipkin front end:
最后,我们可以检查 Zipkin 前端的跟踪:
5. Conclusion
5.结论
In this article, we learned about the ConnectionDetails interface in Spring Boot 3.1. We saw how it can help in externalizing the sensitive connection details to remote services used by the application. It’s worth noting that some of the non-connection-related information is still read from the application.properties file.
在本文中,我们了解了 Spring Boot 3.1 中的 ConnectionDetails 接口。我们看到了它如何帮助外部化应用程序使用的远程服务的敏感连接详细信息。值得注意的是,一些与连接无关的信息仍从 application.properties 文件中读取。
Finally, the code examples used here can be found over on GitHub.