Appearance
Spring In Action 6th:使用配置属性
Spring Boot 提供了一种在应用程序组件上设置属性值的方法,即配置属性。配置属性不过是 Spring 应用程序上下文中 @ConfigurationProperties 注解的 bean 的属性。Spring 将从几个属性源(包括 JVM 系统属性、命令行参数和环境变量)中注入值到 bean 属性中。
1. 微调自动配置
在我们深入研究配置属性之前,有必要先了解 Spring 中以下不同(但相关)的配置类型:
- Bean wiring:在 Spring 应用程序上下文中声明应用程序组件作为 bean 的配置,以及它们应如何相互注入;
- Property injection:在 Spring 应用程序上下文中的 bean 上设置值的配置;
在 Spring 的 XML 和 Java 配置中,这两种类型的配置通常在同一处显式声明。在 Java 配置中,@Bean 注解的方法可能既实例化一个 bean,又设置其属性的值。例如,考虑以下声明了嵌入式 H2 数据库的 DataSource 的 @Bean 方法:
Java
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(H2)
.addScript("taco_schema.sql")
.addScripts("user_data.sql", "ingredient_data.sql")
.build();
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
这里的 addScript() 和 addScripts() 方法设置了一些字符串属性,这些属性是一旦数据源准备就绪应用于数据库的 SQL 脚本的名称。然而,如果你不使用 Spring Boot,你可能会这样配置一个 DataSource bean,但自动配置使得这个方法完全不必要。
如果 H2 依赖项在运行时类路径中可用,那么 Spring Boot 将自动在 Spring 应用程序上下文中创建一个适当的 DataSource bean,该 bean 应用了 SQL 脚本 schema.sql 和 data.sql。但是,如果你想将 SQL 脚本命名为其他内容怎么办?或者,如果你需要指定超过两个 SQL 脚本怎么办?这就是配置属性的用武之地。
但是,在你开始使用配置属性之前,你需要了解这些属性来自哪里。
1.1. 理解 Spring 环境抽象
Spring 环境抽象是所有可配置属性的集中地。它将属性的来源进行了抽象,使得需要这些属性的 bean 可以从 Spring 本身获取它们。Spring 环境从多个属性源中获取属性,包括以下几种:
- JVM 系统属性;
- 操作系统环境变量;
- 命令行参数;
- 应用程序属性配置文件;
然后,它将这些属性聚合到一个可以注入 Spring bean 的单一源中。图 1.1 展示了属性如何从属性源通过 Spring 环境抽象流向 Spring bean。

由 Spring Boot 自动配置的 bean 都可以通过从 Spring 环境中获取的属性进行配置。举一个简单的例子,假设你希望应用程序的底层 Servlet 容器在除默认端口 8080 之外的某个端口监听请求。要做到这一点,你可以在 src/main/resources/application.properties 中设置 server.port 属性来指定一个不同的端口,如下所示:
Properties
server.port=9090我个人更喜欢在设置配置属性时使用 YAML。因此,我可能会在 src/main/resources/application.yml 中设置 server.port 值,而不是使用 application.properties,如下所示:
YAML
server:
port: 90901
2
2
如果你更喜欢外部配置该属性,你也可以在启动应用程序时使用命令行参数指定端口,如下所示:
Bash
$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090如果你希望应用程序始终在特定端口启动,你可以将其设置为操作系统环境变量,如下所示:
Bash
$ export SERVER_PORT=9090Tip:当将属性设置为环境变量时,命名风格略有不同,以适应操作系统对环境变量名称的限制。但这没关系。Spring 能够解决这个问题,并将
SERVER_PORT无误地解释为server.port。
正如我所说,我们有多种方式设置配置属性。实际上,你可以使用几百种配置属性来调整和修改 Spring bean 的行为。你已经看到了一些,比如上例中的 server.port。
1.2. 配置数据源
到目前为止,Taco Cloud 应用程序还未完成,我们现在使用的嵌入式 H2 数据库作为数据源。但是,一旦你将应用程序投入生产,你可能会考虑更持久的数据库解决方案。虽然你可以显式配置自己的 DataSource bean,但这通常是不必要的。相反,通过配置属性配置数据库的 URL 和凭据更简单。例如,如果你开始使用 MySQL 数据库,你可能会将以下配置属性添加到 application.yml 中:
YAML
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword1
2
3
4
5
2
3
4
5
虽然你需要将适当的 JDBC 驱动程序添加到构建中,但你通常不需要指定 JDBC 驱动程序类 —— Spring Boot 可以从数据库 URL 的结构中找出它。但是,如果出现问题,你可以尝试设置 spring.datasource.driver-class-name 属性,如下所示:
YAML
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
driver-class-name: com.mysql.jdbc.Driver1
2
3
4
5
6
2
3
4
5
6
Spring Boot 在自动配置 DataSource bean 时使用这些连接数据。如果 HikariCP 连接池在类路径中可用,DataSource bean 将使用 HikariCP 连接池进行池化。如果不可用,Spring Boot 会查找并使用类路径中的以下其他连接池实现:
- Tomcat JDBC 连接池;
- Apache Commons DBCP2;
虽然这些是通过自动配置可用的唯一连接池选项,但你始终可以显式配置一个 DataSource bean 来使用你喜欢的任何连接池实现。
在前面我们提到可能有一种方法可以指定应用程序启动时运行的数据库初始化脚本。在这种情况下,spring.datasource.schema 和 spring.datasource.data 属性就派上用场了,如下所示:
YAML
spring:
datasource:
schema:
- order-schema.sql
- ingredient-schema.sql
- taco-schema.sql
- user-schema.sql
data:
- ingredients.sql1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
也许你不喜欢显式的数据源配置。相反,你可能更喜欢在 JNDI(Java Naming and Directory Interface)中配置你的数据源,并让 Spring 从那里查找。在这种情况下,通过配置 spring.datasource.jndi-name 来设置你的数据源,如下所示:
YAML
spring:
datasource:
jndi-name: java:/comp/env/jdbc/tacoCloudDS1
2
3
2
3
如果你设置了 spring.datasource.jndi-name 属性,那么其他数据源连接属性(如果设置了的话)将被忽略。
1.3. 配置嵌入式服务器
你已经看到了如何通过设置 server.port 来设置 Servlet 容器的端口。但我没有向你展示的是,如果 server.port 设置为 0,会发生什么,如下所示:
YAML
server:
port: 01
2
2
虽然你明确地将 server.port 设置为 0,但服务器不会在端口 0 上启动。相反,它将在随机选择的可用端口上启动。这在运行自动化集成测试时非常有用,以确保任何并发运行的测试不会在硬编码的端口号上发生冲突。
但是,底层服务器的内容不仅仅是一个端口。你需要做的最常见的事情之一就是设置底层容器来处理 HTTPS 请求。要做到这一点,你必须首先使用 JDK 的 keytool 命令行工具创建一个密钥库,如下所示:
Bash
$ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA你会被问到关于你的名字和组织的几个问题,其中大部分都不相关。但是当被问到密码时,记住你选择的内容。为了这个例子,我选择了 letmein 作为密码。
接下来,你需要设置一些属性来启用嵌入式服务器中的 HTTPS。你可以在命令行中指定它们,但那会非常不方便。相反,你可能会在 application.properties 或 application.yml 文件中设置它们。在 application.yml 中,属性可能如下所示:
YAML
server:
port: 8443
ssl:
key-store: file:///path/to/mykeys.jks
key-store-password: letmein
key-password: letmein1
2
3
4
5
6
2
3
4
5
6
这里的 server.port 属性设置为 8443,这是开发 HTTPS 服务器的常见选择。server.ssl.key-store 属性应设置为创建密钥库文件的路径。这里显示的是一个 file:/// URL,用于从文件系统加载它,但是如果你将它打包在应用程序 JAR 文件中,你将使用 classpath: URL 来引用它。server.ssl.key-store-password 和 server.ssl.key-password 属性都设置为创建密钥库时给出的密码。
有了这些属性,你的应用程序应该在 8443 端口监听 HTTPS 请求。根据你使用的浏览器的不同,你可能会遇到关于服务器无法验证其身份的警告。在开发过程中从 localhost 提供服务时,这不是什么需要担心的问题。
1.4. 配置日志
大多数应用程序都提供了某种形式的日志记录。即使你的应用程序没有直接记录任何内容,你的应用程序使用的库肯定会记录它们的活动。
默认情况下,Spring Boot 通过 Logback 配置日志记录,以 INFO 级别写入控制台。你可能已经在运行应用程序和其他示例时在应用程序日志中看到了大量的 INFO 级别条目。但作为提醒,下面是一个日志样本,显示了默认的日志格式:
Text
2021-07-29 17:24:24.187 INFO 52240 --- [nio-8080-exec-1] com.example.demo.Hello Here's a log entry.
2021-07-29 17:24:24.187 INFO 52240 --- [nio-8080-exec-1] com.example.demo.Hello Here's another log entry.
2021-07-29 17:24:24.187 INFO 52240 --- [nio-8080-exec-1] com.example.demo.Hello And here's one more.1
2
3
2
3
为了完全控制日志配置,你可以在类路径的根目录(在 src/main/resources)创建一个 logback.xml 文件。以下是一个你可能会使用的简单 logback.xml 文件的示例:
XML
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="root" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
对于这个新的配置,之前的示例日志条目可能会看起来像这样:
Text
17:25:09.088 [http-nio-8080-exec-1] INFO com.example.demo.Hello - Here's a log entry.
17:25:09.088 [http-nio-8080-exec-1] INFO com.example.demo.Hello - Here's another log entry.
17:25:09.088 [http-nio-8080-exec-1] INFO com.example.demo.Hello - And here's one more.1
2
3
2
3
对日志配置进行的最常见的更改是更改日志级别,也许还会指定应将日志写入哪个文件。有了 Spring Boot 配置属性,你可以在不创建 logback.xml 文件的情况下进行这些更改。
要设置日志级别,你需要创建以 logging.level 为前缀的属性,后面跟着你想要设置日志级别的 logger 的名称。例如,假设你想将根日志级别设置为 WARN,但是将 Spring Security 的日志记录在 DEBUG 级别。下面在 application.yml 中的条目将为你处理这个问题:
YAML
logging:
level:
root: WARN
org:
springframework:
security: DEBUG1
2
3
4
5
6
2
3
4
5
6
你也可以选择将 Spring Security 的包名折叠为一行,以便更容易阅读,如下所示:
YAML
logging:
level:
root: WARN
org.springframework.security: DEBUG1
2
3
4
2
3
4
现在假设你想要将日志条目写入位于 /var/logs/ 的 TacoCloud.log 文件。logging.file.path 和 logging.file.name 属性可以帮助实现这一点,如下所示:
YAML
logging:
file:
path: /var/logs/
file: TacoCloud.log
level:
root: WARN
org.springframework.security: DEBUG1
2
3
4
5
6
7
2
3
4
5
6
7
假设应用程序具有对 /var/logs/ 的写入权限,那么日志条目将被写入 /var/logs/TacoCloud.log。默认情况下,日志文件在达到 10 MB 大小时会进行轮换。
1.5. 使用特殊的属性值
在设置属性时,你不必将它们的值声明为硬编码的字符串和数字值。相反,你可以从其他配置属性中派生它们的值。
例如,假设你想要设置一个名为 greeting.welcome 的属性,以回显另一个名为 spring.application.name 的属性的值。为了实现这一点,你可以在设置 greeting.welcome 时使用 ${} 占位符标记,如下所示:
YAML
greeting:
welcome: ${spring.application.name}1
2
2
你甚至可以将该占位符嵌入到其他文本中,如下所示:
YAML
greeting:
welcome: You are using ${spring.application.name}.1
2
2
正如你所看到的,使用配置属性配置 Spring 自己的组件使得将值注入到这些组件的属性中以及微调自动配置变得容易。配置属性并不仅限于 Spring 创建的 bean。我们也可以创建自己的配置属性。
2. 创建自己的配置属性
为了支持配置属性的属性注入,Spring Boot 提供了 @ConfigurationProperties 注解。当它被放置在任何 Spring bean 上时,它指定该 bean 的属性可以从 Spring 环境中的属性中注入。
为了演示 @ConfigurationProperties 是如何工作的,假设你已经在 OrderController 中添加了以下方法来列出经过身份验证的用户的过去订单:
Java
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user));
return "orderList";
}1
2
3
4
5
6
2
3
4
5
6
除此之外,你还在 OrderRepository 中添加了下一个必要的 findByUserOrderByPlacedAtDesc() 方法:
Java
public interface OrderRepository
extends CrudRepository<TacoOrder, Long> {
List<TacoOrder> findByUserOrderByPlacedAtDesc(User user);
}1
2
3
4
2
3
4
假设你想限制显示的订单数量为最近的 20 个订单。你可以如下更改 ordersForUser() 方法:
Java
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, 20);
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}1
2
3
4
5
6
7
2
3
4
5
6
7
以及对 OrderRepository 的相应更改,如下所示:
Java
List<TacoOrder> findByUserOrderByPlacedAtDesc(User user, Pageable pageable);尽管这个方法运行得很好,但如果你后来决定 20 个订单太多,你决定将其改为 10 个呢?因为它是硬编码的,你必须重新构建并重新部署应用程序。
你可以使用自定义配置属性来设置页面大小,而不是硬编码页面大小。首先,你需要向 OrderController 添加一个名为 pageSize 的新属性,然后使用 @ConfigurationProperties 注解 OrderController,如下所示:
Java
@Controller
@RequestMapping("/orders")
@SessionAttributes("tacoOrder")
@ConfigurationProperties(prefix = "taco.orders")
public class OrderController {
...
private int pageSize = 20;
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, pageSize);
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上述最重要的更改是添加了 @ConfigurationProperties 注解。它的 prefix 属性设置为 taco.orders,这意味着在设置 pageSize 属性时,你需要使用一个名为 taco.orders.page-size 的配置属性。
新的 pageSize 属性默认为 20,但你可以通过设置一个 taco.orders.page-size 属性轻松地将其更改为你想要的任何值。例如,你可以在 application.yml 中像这样设置此属性:
YAML
taco:
orders:
page-size: 101
2
3
2
3
或者,如果你需要在生产环境中快速更改,你可以通过将 taco.orders.page-size 属性设置为以下环境变量来实现,而无需重新构建和重新部署应用程序:
Bash
$ export TACO_ORDERS_PAGESIZE=10可以通过任何可以设置配置属性的方式来调整最近订单页面的页面大小。接下来,我们将看看如何在属性持有者中设置配置数据。
2.1. 定义配置属性持有者
并没有规定必须在控制器或任何其他特定类型的 bean 上设置 @ConfigurationProperties。实际上,@ConfigurationProperties 通常放在应用程序中唯一的目的是持有配置数据的 bean 上。这将配置特定的细节从控制器和其他应用程序类中剥离出来。它还使得在可能使用该信息的几个 bean 之间共享常见的配置属性变得容易。
以 OrderController 中的 pageSize 属性为例,你可以将其提取到一个单独的类中。比如 OrderProps 类:
Java
@Component
@ConfigurationProperties(prefix = "taco.orders")
@Data
public class OrderProps {
private int pageSize = 20;
}1
2
3
4
5
6
2
3
4
5
6
就像你在 OrderController 中所做的那样,pageSize 属性默认为 20,OrderProps 使用 @ConfigurationProperties 注解,前缀为 taco.orders。它还使用 @Component 注解,以便 Spring 组件扫描会自动发现它,并在 Spring 应用程序上下文中将其创建为一个 bean。这一点很重要,因为下一步是将 OrderProps bean 注入到 OrderController 中。
配置属性持有者并没有什么特别的。它们是从 Spring 环境中注入其属性的 bean。它们可以被注入到任何需要这些属性的其他 bean 中。对于 OrderController,这意味着从 OrderController 中移除 pageSize 属性,而是注入和使用 OrderProps bean,如下所示:
Java
@Slf4j
@Controller
@RequestMapping("/orders")
@SessionAttributes("tacoOrder")
public class OrderController {
private final OrderRepository orderRepo;
private final OrderProps props;
public OrderController(OrderRepository orderRepo,
OrderProps props) {
this.orderRepo = orderRepo;
this.props = props;
}
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, props.getPageSize());
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
...
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
现在,OrderController 不再负责处理自己的配置属性。这使得 OrderController 中的代码稍微整洁一些,并且允许你在任何其他可能需要它们的 bean 中重用 OrderProps 中的属性。此外,你正在一个地方收集与订单相关的配置属性:OrderProps 类。如果你需要添加、删除、重命名或以其他方式更改其中的属性,你只需要在 OrderProps 中应用这些更改。而且,出于测试目的,很容易直接在特定于测试的 OrderProps 上设置配置属性,并在测试之前将其提供给控制器。
例如,假设你在几个其他 bean 中使用 pageSize 属性时,你决定最好对该属性应用一些验证,以将其值限制在不少于 5 且不超过 25。如果没有持有者 bean,你将不得不将验证注解应用到 OrderController、pageSize 属性以及所有其他使用该属性的类上。但是,因为你已经将 pageSize 提取到 OrderProps 中,你只需要对 OrderProps 进行更改,如下所示:
Java
...
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Component
@ConfigurationProperties(prefix = "taco.orders")
@Data
@Validated
public class OrderProps {
@Min(value = 5, message = "must be between 5 and 25")
@Max(value = 25, message = "must be between 5 and 25")
private int pageSize = 20;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
尽管你可以同样轻松地将 @Validated、@Min 和 @Max 注解应用到 OrderController(以及可以注入 OrderProps 的任何其他 bean),但这只会使 OrderController 更加混乱。有了一个配置属性持有者 bean,你已经在一个地方收集了配置属性的具体信息,使得需要这些属性的类相对干净。
2.2. 声明配置属性元数据
根据你的 IDE,你可能已经注意到在 application.yml(或 application.properties)中的 taco.orders.page-size 条目有一个警告,说的是类似于 Unknown Property ‘taco’ 的内容。这个警告出现是因为关于你刚刚创建的配置属性的元数据缺失。当我在 Spring Tool Suite 中将鼠标悬停在属性的 taco 部分时,图 2.1 显示了这是什么样子:

配置属性元数据完全是可选的,并不会阻止配置属性的工作。但是,元数据对于提供一些关于配置属性的最小文档,特别是在 IDE 中,可能是有用的。例如,当我将鼠标悬停在 spring.security.user.password 属性上时,我看到了图 2.2 中显示的内容。尽管你得到的悬停帮助是最小的,但它可能足以帮助理解属性的用途以及如何使用它。

为了帮助那些可能使用你定义的配置属性的人 —— 甚至可能是你自己(通常来说),创建一些关于这些属性的元数据是个好主意。至少,它可以消除 IDE 中那些烦人的黄色警告。
要为你的自定义配置属性创建元数据,你需要在 META-INF 下(例如,在项目的 src/main/resources/META-INF 下)创建一个名为 additional-spring-configuration-metadata.json 的文件。
如果你正在使用 Spring Tool Suite,那么有一个快速修复选项可以创建缺失的属性元数据。将你的光标放在有缺失元数据警告的行上,然后用 Mac 上的 CMD + 1 或 Windows 和 Linux 上的 Ctrl + 1 打开快速修复弹出窗口(见图 2.3):

然后选择 Create Metadata for … 选项为该属性添加一些元数据。这个快速修复将在 META-INF 目录下创建一个 additional-spring-configuration-metadata.json 文件(如果它还不存在),并用一些 page-size 属性的元数据填充它,如下所示:
JSON
{
"properties": [
{
"name": "taco.orders.page-size",
"type": "java.lang.Integer",
"description": "Sets the maximum number of orders to display in a list."
}
]
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Note
需要在
pom.xml中添加spring-boot-configuration-processor引用:XML<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>1
2
3
4
5
有了这个元数据,警告应该就消失了。更重要的是,如果你将鼠标悬停在 taco.orders.page-size 属性上,你会看到图 2.4 中显示的描述。

此外,如图 2.5 所示,你会得到 IDE 的自动完成帮助,就像 Spring 提供的配置属性一样。

如果你需要为不同的部署环境配置不同的属性怎么办?让我们看看如何使用 Spring 配置文件(profiles)来设置特定于环境的配置。
3. 使用 profile 进行配置
当应用程序部署到不同的运行时环境时,通常有一些配置细节会有所不同。例如,数据库连接的细节在开发环境中可能与质量保证环境中的不同,而在生产环境中又有所不同。一种在一个环境中对属性进行唯一配置的方法是使用环境变量来指定配置属性,而不是在 application.properties 和 application.yml 中定义它们。
例如,在开发过程中,你可以依赖自动配置的嵌入式 H2 数据库。但在生产环境中,你可以像下面这样设置数据库配置属性作为环境变量:
Bash
% export SPRING_DATASOURCE_URL=jdbc:mysql://localhost/tacocloud
% export SPRING_DATASOURCE_USERNAME=tacouser
% export SPRING_DATASOURCE_PASSWORD=tacopassword1
2
3
2
3
虽然这种方法可行,但是将一个或两个以上的配置属性指定为环境变量有些麻烦。此外,没有好的方法来跟踪环境变量的更改,或者如果出现错误,轻松地回滚更改。
相反,我更喜欢利用 Spring 的配置文件。配置文件是一种条件配置,其中不同的 beans、配置类和配置属性根据运行时激活的配置文件进行应用或忽略。
例如,假设出于开发和调试的目的,你想使用嵌入式的 H2 数据库,并且你希望将 Taco Cloud 代码的日志级别设置为 DEBUG。但在生产环境中,你希望使用外部的 MySQL 数据库,并将日志级别设置为 WARN。在开发情况下,不设置任何数据源属性并获取自动配置的 H2 数据库就足够简单了。至于调试级别的日志,你可以在 application.yml 中将 tacos 基础包的 logging.level.tacos 属性设置为 DEBUG,如下所示:
YAML
logging:
level:
tacos: DEBUG1
2
3
2
3
这正是你在开发过程中所需要的。但是,如果你要在没有对 application.yml 进行进一步更改的情况下,在生产环境中部署这个应用程序,你仍然会在 tacos 包中有调试日志,并且有一个嵌入式的 H2 数据库。你需要的是定义一个适合生产环境的配置文件。
3.1. 定义特定 profile 的属性
定义特定配置文件的属性的一种方法是创建另一个只包含生产环境属性的 YAML 或 properties 文件。文件的名称应遵循以下约定:application-{profile name}.yml 或 application-{profile name}.properties。然后,你可以指定适合该配置文件的配置属性。例如,你可以创建一个名为 application-prod.yml 的新文件,其中包含以下属性:
YAML
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
指定特定配置文件的属性的另一种方法只适用于 YAML 配置。它涉及在 application.yml 中将特定配置文件的属性与非配置文件的属性放在一起,由三个连字符和 spring.profiles 属性来命名配置文件。以这种方式将生产属性应用到 application.yml 时,整个 application.yml 将如下所示:
YAML
logging:
level:
tacos: DEBUG
---
spring:
profiles: prod
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如你所见,这个 application.yml 文件被一组三连字符(---)分为两部分。第二部分为 spring.profiles 指定了一个值,表明接下来的属性适用于 prod 配置文件。另一方面,第一部分并没有为 spring.profiles 指定一个值。因此,它的属性对所有配置文件都是通用的,或者如果活动配置文件没有设置属性,则为默认值。
无论应用程序运行时哪些配置文件是活动的,由于在默认配置文件中设置的属性,tacos 包的日志级别都将被设置为 DEBUG。但是,如果名为 prod 的配置文件是活动的,那么 logging.level.tacos 属性将被覆盖为 WARN。同样,如果 prod 配置文件是活动的,那么数据源属性将被设置为使用外部的 MySQL 数据库。
你可以通过创建额外的以 application-{profile name}.yml 或 application-{profile name}.properties 命名的 YAML 或 properties 文件,为你需要的尽可能多的配置文件定义属性。或者,如果你愿意,你可以在 application.yml 中再输入三个连字符,以及另一个 spring.profiles 属性来指定配置文件名称。然后添加你需要的所有特定配置文件的属性。虽然这两种方法都没有优势,但你可能会发现,当属性数量较少时,将所有配置文件配置放在一个 YAML 文件中效果最好,而当你有大量属性时,为每个配置文件使用独立的文件更好。
3.2. 激活 profile
如何使一个配置文件变为活动状态呢?使配置文件变为活动状态的所有操作就是将其包含在给 spring.profiles.active 属性的配置文件名称列表中。例如,你可以在 application.yml 中像这样设置它:
YAML
spring:
profiles:
active:
- prod1
2
3
4
2
3
4
但这可能是设置活动配置文件的最糟糕的方式。如果你在 application.yml 中设置了活动配置文件,那么该配置文件就会成为默认配置文件,你将无法实现使用配置文件将生产特定属性与开发属性分开的所有好处。相反,我建议你使用环境变量设置活动配置文件。在生产环境中,你可以像下面这样设置 SPRING_PROFILES_ACTIVE:
Bash
% export SPRING_PROFILES_ACTIVE=prod从此时起,部署到该机器的任何应用程序都将激活 prod 配置文件,相应的配置属性将优先于默认配置文件中的属性。
如果你以可执行 JAR 文件的形式运行应用程序,你也可以像下面这样使用命令行参数设置活动配置文件:
Bash
% java -jar taco-cloud.jar --spring.profiles.active=prod注意,spring.profiles.active 属性名称中包含了复数词 profiles。这意味着你可以指定多个活动配置文件。通常,这是通过一个逗号分隔的列表来设置的,就像在设置环境变量时一样,如下所示:
Bash
% export SPRING_PROFILES_ACTIVE=prod,audit,ha但在 YAML 中,你可以像下面这样将其指定为一个列表:
YAML
spring:
profiles:
active:
- prod
- audit
- ha1
2
3
4
5
6
2
3
4
5
6
事实证明,配置文件不仅对于在 Spring 应用程序中有条件地设置配置属性有用。让我们看看如何声明特定于活动配置文件的 beans。
3.3. 有条件地使用 profile 创建 bean
有时,为不同的配置文件(profile)提供一组独特的 beans 是很有用的。通常,无论哪个配置文件是活动的,Java 配置类中声明的任何 bean 都会被创建。但是,假设你需要一些 beans 只在某个特定的配置文件是活动的时候才被创建。在这种情况下,你可以使用 @Profile 注解来指定 beans 只适用于给定的配置文件。
例如,假设你在 Taco-CloudApplication 中声明了一个 CommandLineRunner bean,当应用程序启动时,它用于加载嵌入式数据库的配料数据。这对于开发来说是很好的,但在生产应用程序中是不必要的(甚至是不希望的)。为了防止每次在生产部署中启动应用程序时都加载配料数据,你可以像下面这样使用 @Profile 注解 CommandLineRunner bean 方法:
Java
@Bean
@Profile("dev")
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}1
2
3
4
5
6
2
3
4
5
6
或者,假设你需要在 dev 配置文件或 qa 配置文件是活动的时候创建 CommandLineRunner。在这种情况下,你可以列出应该创建 bean 的配置文件,如下所示:
Java
@Bean
@Profile({"dev", "qa"})
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}1
2
3
4
5
6
2
3
4
5
6
或者只有当 prod 配置文件非活动时才创建 CommandLineRunner bean,那就更方便了。在这种情况下,你可以像下面这样应用 @Profile:
Java
@Bean
@Profile("!prod")
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}1
2
3
4
5
6
2
3
4
5
6
你也可以在整个用 @Configuration 注解的类上使用 @Profile。例如,假设你将 CommandLineRunner bean 提取到一个名为 DevelopmentConfig 的单独配置类中。然后,你可以像下面这样用 @Profile 注解 DevelopmentConfig:
Java
@Profile({"!prod", "!qa"})
@Configuration
public class DevelopmentConfig {
@Bean
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
在这里,只有当 prod 或 qa 配置文件(profile)不是活动的时候,CommandLineRunner bean(以及 DevelopmentConfig 中定义的任何其他 beans)才会被创建。
4. 小结
- 我们可以用
@ConfigurationProperties注解 Spring beans,以启用从多个属性源中注入值; - 配置属性可以在命令行参数、环境变量、JVM 系统属性、属性文件或 YAML 文件中设置,还有其他选项;
- 使用配置属性来覆盖自动配置设置,包括指定数据源 URL 和日志级别的能力;
- 可以使用 Spring 配置文件(profiles)和属性源,根据活动配置文件有条件地设置配置属性;