5.1 微调自动配置
5.1 微调自动配置
在我们深入研究配置属性之前,有必要确定在 Spring 中有两种不同(但相关)的配置
- Bean wiring —— 它声明应用程序组件将在 Spring 应用程序上下文中作为 bean 创建,以及它们应该如何相互注入。
- Property injection —— 在 Spring 应用程序上下文中设置 bean 的值。
在 Spring 的 XML 和基于 Java 的配置中,这两种类型的配置通常在同一个地方显式地声明。在 Java 配置中,@Bean 注解的方法可能实例化一个 bean,然后设置其属性的值。例如,考虑下面的 @Bean 方法,它为嵌入式 H2 数据库声明了一个数据源:
@Bean
public DataSource dataSource() {
return new EmbeddedDataSourceBuilder()
.setType(H2)
.addScript("taco_schema.sql")
.addScripts("user_data.sql", "ingredient_data.sql")
.build();
}
这里的 addScript() 和 addScripts() 方法设置了一些带有 SQL 脚本名称的字符串属性,这些 SQL 脚本应该在数据源准备好后应用到数据库中。如果不使用 Spring Boot,那么这就是配置 DataSource bean 的方式,而自动配置使此方法完全没有必要。
如果 H2 依赖项在运行时类路径中可用,那么 Spring Boot 将在 Spring 应用程序上下文中自动创建适当的数据源 bean。bean 应用于 schema.sql 和 data.sql 脚本的读取。
但是,如果希望将 SQL 脚本命名为其他名称呢?或者,如果需要指定两个以上的 SQL 脚本怎么办?这就是配置属性的用武之地。但是在开始使用配置属性之前,需要了解这些属性的来源。
5.1.1 理解 Spring 环境抽象
Spring 环境抽象是任何可配置属性的一站式商店。它抽象了属性的起源,以便需要这些属性的 bean 可以从 Spring 本身使用它们。Spring 环境来自几个属性源,包括:
-
JVM 系统属性
-
操作系统环境变量
-
命令行参数
-
应用程序属性配置文件
然后,它将这些属性聚合到单一的源中,从这个源中可以注入 Spring bean。图 5.1 演示了来自属性源的属性是如何通过 Spring 环境抽象流到 Spring bean 中的。
![图 5.1](E:\Document\spring-in-action-v5-translate\第一部分 Spring 基础\第五章 使用配置属性\图 5.1.jpg)
图 5.1 Spring 环境从属性源获取属性,并使它们能够在应用程序上下文中的 bean 获取
通过 Spring Boot 自动配置的 bean 都可以通过从 Spring 环境中提取的属性进行配置。作为一个简单的例子,假设希望应用程序的底层 servlet 容器侦听某些端口上的请求,而不是默认端口 8080。为此,通过在 src/main/resources/application.properties 文件中的 server.port 属性来指定一个不同的接口,如下所示:
server.port=9090
就我个人而言,我更喜欢在设置配置属性时使用 YAML。因此,我可能设置在 /src/main/resources/application.yml 文件中的 server.port 的值,而不是使用 application.properties 文件,如下所示:
server:
port: 9090
如果希望在外部配置该属性,还可以在启动应用程序时使用命令行参数指定端口:
$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
如果想让应用程序总是在一个特定的端口上启动,可以把它设置为一个操作系统环境变量:
$ export SERVER_PORT=9090
注意,在将属性设置为环境变量时,命名风格略有不同,以适应操作系统对环境变量名称的限制。Spring 能够将其分类并将 SERVER_PORT 转译为 server.port。
正如我所说的,有几种设置配置属性的方法。当我们讲到第 14 章的时候,你会看到在一个集中的配置服务器中设置配置属性的另一种方法。实际上,可以使用几百个配置属性来调整 Spring bean 的行为。你已经看到了一些:本章中的 server.port 和前一章的 security.user.name 和 security.user.password。
在本章中不可能测试所有可用的配置属性。尽管如此,让我们来看看一些可能经常遇到的最有用的配置属性。我们将从几个属性开始,这些属性允许你调整自动配置的数据源。
5.1.2 配置数据源
此时,Taco Cloud 应用程序仍未完成,但是在准备部署应用程序之前,还有几个章节要处理一些问题。因此,作为数据源使用的嵌入式 H2 数据库非常适合目前为止需要的一切。但是,一旦将应用程序投入生产,可能需要考虑一个更持久的数据库解决方案。
虽然可以显式地配置 DataSource bean,但这通常是不必要的。相反,通过配置属性为数据库配置 URL 和凭据更简单。例如,如果打算开始使用 MySQL 数据库,可以将以下配置属性添加到 application.yml:
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacodb
password: tacopassword
虽然需要将适当的 JDBC 驱动程序添加到构建中,但通常不需要指定 JDBC 驱动程序类;Spring Boot 可以从数据库 URL 的结构中找到它。但如果有问题,可以试着设置 spring.datasource.schema 和 spring.datasource.data 属性:
spring:
datasource:
schema:
- order-schema.sql
- ingredient-schema.sql
- tao-schema.sql
- user-schema.sql
data:
- ingredients.sql
可能显式数据源配置不是你的风格。相反,你可能更喜欢在 JNDI 中配置数据源,并让 Spring 从那里查找它。在这种情况下,通过配置 spring.datasource.jndi-name 来设置数据源:
spring:
datasource:
jndi-name: java:/comp/env/jdbc/tacoCloudDS
如果设置了 spring.datasource.jndi-name 属性,那么其他数据源的连接属性(如果设置了)会被忽略。
5.1.3 配置嵌入式服务器
已经看到如何通过设置 server.port 来设置 servlet 容器。还没有让你看到的是,如果把 server.port 设置为 0 会发生什么:
server:
port: 0
尽管正在显式地设置 server.port 为 0,但是服务器不会在端口 0 上启动。相反,它将从随机选择的可用端口启动。这在运行自动化集成测试以确保任何并发运行的测试不会在硬编码端口号上发生冲突时非常有用。在第 13 章中将看到,当不关心应用程序启动于哪个端口时,它也很有用,因为它是一个将从服务注册表中查找的微服务。
但是底层服务器不仅仅是一个端口。需要对底层容器做的最常见的事情之一是将其设置为处理 HTTPS 请求。要做到这一点,你必须做的第一件事是通过使用 JDK 的 keytool 命令行工具创建一个密钥存储:
$ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
你将会被问到几个关于你的名字和公司的问题,这些问题大部分都是无关紧要的。但当被要求输入密码时,记住你的密码。对于本例,我选择 letmein 作为密码。
接下来,需要设置一些属性,用于在嵌入式服务器中启用 HTTPS。可以在命令行中指定它们,但是那样会非常不方便。相反,可能会在 application.properties 或 application.yml 文件中设置它们。在 application.yml 中,属性可能是这样的:
server:
port: 8443
ssl:
key-store: file:///path/to/mykeys.jks
key-store-password: letmein
key-password: letmein
在这里 server.port 属性设置为 8443,这是开发 HTTPS 服务器的常用选择。server.ssl.key-store 属性设置为创建密钥存储库文件的路径。这里显示了一个 file://
URL 来从文件系统加载它,但是如果将它打包到应用程序 JAR 文件中,将使用一个 classpath:
URL 来引用它。同时 server.ssl.key-store-password 和 server.ssl.key-password 属性都被设置为创建密钥存储时指定的密码值。
有了这些属性,应用程序应该侦听端口 8443 上的 HTTPS 请求。根据使用的浏览器,可能会遇到服务器无法验证其身份的警告。在开发期间从本地主机提供服务时,这没有什么可担心的。
5.1.4 配置日志
大多数应用程序都提供某种形式的日志记录。即使应用程序没有直接记录任何内容,应用程序使用的库也肯定会记录它们的活动。
默认情况下,Spring Boot 通过 Logback 配置日志,默认为 INFO 级别,然后写入控制台。在运行应用程序和其他示例时,可能已经在应用程序日志中看到了大量的 INFO 级别的日志条目。
要完全控制日志配置,可以在类路径的根目录(在 src/main/resources 中)创建 log .xml 文件。下面是一个简单的 log .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>
除了用于日志的模式外,Logback 配置或多或少与没有 log .xml 文件时得到的默认配置相同。但是通过编辑 logback.xml,可以完全控制应用程序的日志文件。
注意:logback.xml 中包含的具体内容超出了本书的范围。有关更多信息,请参阅 Logback 的文档。
对日志配置最常见的更改是更改日志级别,可能还会指定应该写入日志的文件。使用 Spring Boot 配置属性,可以在不创建 log .xml 文件的情况下进行这些更改。
要设置日志记录级别,需要创建以 logging.level 为前缀的属性,后面接上要为其设置日志级别的日志记录器的名称。例如,假设想将 root 日志级别设置为 WARN,但是将 Spring 安全日志设置为 DEBUG 级别。可以像下面这样设置:
logging:
level:
root: WARN
org:
springframework:
security: DEBUG
另外,可以将 Spring Security 包的名称折叠成一行,以便于阅读:
logging:
level:
root: WARN
org:
springframework.security: DEBUG
现在,假设希望将日志条目写入位于 /var/logs/ 文件夹下的 TacoCloud.log 文件。loggin.path 和 logging.file 属性可以帮助实现这一点:
logging:
path: /var/logs/
file: TacoCloud.log
level:
root: WARN
org:
springframework:
security: DEBUG
假设应用程序对 /var/logs/ 文件夹有写权限,那么日志将被写到 /var/logs/TacoCloud.log 文件中。默认情况下,日志文件在大小达到 10 MB 时就会进行循环写入。
5.1.5 使用特殊的属性值
在设置属性时,不限于将它们的值声明为硬编码的字符串和数值。相反,可以从其他配置属性派生它们的值。
例如,假设(不管出于什么原因)想要设置一个名为 greeting.welcome 的属性,用于返回另一个名为 spring.application.name 的属性的值。为此,在设置 greeting.welcome 时可以使用 ${}
占位符标记:
greeting:
welcome: ${spring.application.name}
你甚至可以把这个占位符嵌入到其他文本中:
greeting:
welcome: You are using ${spring.application.name}
正如你所看到的,使用配置属性配置 Spring 自己的组件可以很容易地将值注入这些组件的属性并调整自动配置。配置属性并不专属于 Spring 创建的 bean。只需稍加努力,就可以利用你自己的 bean 中的配置属性。接下来让我们来看看怎么做。