5. 1 细 粒度 的 自动 配置
Spring 中有 两种 不同( 但 相关) 的 配置。
- bean 装配: 声明 在 Spring 应用 上下 文中 创建 哪些应用 组件 以及 它们 之间 如何 互相 注入 的 配置。
- 属性 注入: 设置 Spring 应用 上下 文中 bean 的 值 的 配置。
在 Spring 的 XML 方式 和 基于 Java 的 配置 中, 这 两种 类型 的 配置 通常 会在 同一个 地方 显 式 声明。(配置文件)
在 基于 Java 的 配置 中, 带有@ Bean 注解 的 方法 一般 会 同时 初始化 bean 并立 即为 它的 属性 设置 值。(代码注解)
5. 1. 1 理解 Spring 的 环境 抽象
Spring 环境 会 拉 取 多个 属性 源, 包括:
- JVM 系统 属性;
- 操作系统 环境 变量;
- 命令行 参数;
- 应用 属性 配置文件。
它 会 将 这些 属性 聚合 到 一个 源 中, 通过 这个 源 可以 注入 到 Spring 的 bean 中。
Spring Boot 自动 配置 的 bean 都可以 通过 Spring 环境 提取 的 属性 进行 配置。 举个 简单 的 例子, 假设 我们 希望 应用 底层 的 Servlet 容器 使用 另外 一个 端口 监听 请求, 而 不再 使用 8080。
我们 可以 在“ src/ main/ resources/ application. properties” 中将 server. port 设置 成 一个 不同 的 端口,
server. port= 909
也可以在“ src/ main/ resources/ application. yml” 中 设置 server. port 的 值,
server: port:
9090
如果 你 喜欢 在外 部 配置 该 属性, 那么 可以 在 使用 命令行 参数 启动 应用 的 时候 指定 端口:
$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
如果 你 希望 应用 始终 在 一个 特定 的 端口 启动, 那么 可以 通过 操作系统 的 环境 变量 进行 一次 性的 设置:
$ export SERVER_ PORT= 9090
上面这些配置的效果都是一样的
5. 1. 2 配置 数据 源
如果 你想 要 开始 使用 MySQL 数据库,
spring: datasource: url: jdbc:mysql://localhost/tacocloud username: tacouser password: tacopassword
如果 在 类 路径 中 存在 Tomcat 的 JDBC 连接 池, DataSource 将 使用 该 连接 池。 否则, Spring Boot 将 会在 类 路径 下 尝试 查找 并使 用 如下 的 连接 池 实现:
- HikariCP
- Commons DBCP 2
5. 1. 3 配置 嵌入 式 服务器
我们 已经 看到 过 如何 使用 server. port 属性 来 配置 servlet 容器 的 端口。 但是, 我还 没有 展示 将 server. port 设置 为 0 将会 出现 什么 状况:
server:
port: 0
尽管 我们将 server. port 属性 显 式 设置 成了 0, 但是 服务器 并不 会 真的 在 端口 0 上 启动。 相反, 它 会 任选 一个 可用 的 端口。 在 我们 运行 自动化 集成 测试 的 时候, 这 会 非常 有用, 因为 这样 能够 保证 并发 运行 的 测试 不会 与 硬 编码 的 端口 号 冲突。
这里有一部分,关于java配置生成HTTPS证书部分的内容,我觉得挺有用的,但是我没试过,仅做记录:
我们 对 底层 容器 常见 的 一项 设置 就是 让 它 处理 HTTPS 请求。 为了 实现 这一点, 我们 首先 要 使用 JDK 的 keytool 命令行 工具 生成 keystore:
$ 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 属性 应该 设置 为 我们 所 创建 的 keystore 文件 的 路径。 在这里, 它 使用 了 file:// URL, 因此 会在 文件 系统 中 加载, 但是, 如果 你 需要 将它 打包 到 一个 应用 JAR 文件 中, 就 需要 使用“ classpath:” URL 来 引用 它。 server. ssl. key- store- password 和 server. ssl. key- password 属性 都 设置 成了 创建 keystore 时 所 设置 的 密码。
结束HTTPS部分
5. 1. 4 配置 日志
默认 情况下, Spring Boot 通过 Logback 配置 日志, 日志 会 以 INFO 级别 写入 到 控制 台中。
为了 完全 控制 日志 的 配置, 我们 可 以在 类 路径 的 根 目录 下( 在 src/ main/ resources 中) 创建 一个 logback. xml 文件。
如下 是一 个 简单 logback. 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>
在 日志 配置 方面, 你 可能 遇到 的 常见 变更 就是 修改 日志 级别 和 指定 日志 写入 到 哪个 文件 中。 借助 Spring Boot 的 配置 属性 功能, 我们 不用 创建 logback. xml 文件 就能 完成 这些 变更。
要 设置 日志 级别, 我们 可以 创建 以 logging. level 作为 前缀 的 属性, 随后 紧 跟着 的 是我 们 想要 设置 日志 级别 的 logger。
logging:
level:
root: WARN
org.springframework.security: DEBUG
现在, 假设 我们 想要 将 日志 条目 写入 到“/ var/ logs/” 中的 TacoCloud. log 文件 中。 logging. path 和 logging. file 文件 可以 按照 如下 形式 进行 设置:
logging:
path: /var/logs/
file: TacoCloud.log
level:
root: WARN
org.springframework.security: DEBUG
5. 1. 5 使用 特定 的 属性 值
在 设置 属性 的 时候, 我们 并非 必须 要将 它们 的 值 设置 为 硬 编码 的 String 或 数值。 其实, 我们 还可以 从其 他的 配置 属性 派生 值。
例如, 假设( 不管 基于 什么 原因) 我们 想要 设置 一个 名为 greeting. welcome 的 属性, 它的 值 来源于 名为 spring. application. name 的 另一个 属性。 为了 实现 该 功能, 在 设置 greeting. welcome 的 时候, 我们 可以 使用${} 占位符 标记:
greeting:
welcome: ${spring.application.name}
我们 甚至 可以 将 占位符 嵌入 到 其他 文本 中:
greeting:
welcome: You are using ${spring.application.name}.
总之 配置 属性 并不 专 属于 Spring 创建 的 bean。
5. 2 创建 自己的 配置 属性
为了 支持 配置 属性 的 注入, Spring Boot 提供 了@ ConfigurationProperties 注解。 将它 放到 Spring bean 上 之后, 它 就 会为 该 bean 中 那些 能够 根据 Spring 环境 注入 值 的 属性 赋值。
例如, 我们 可以 在 application. yml 中 按照 如下 的 方式 设置 该 属性:
taco:
orders:
pageSize: 10
使用方法:
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController {
private int pageSize = 20;
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
代码的 pageSize 值 默认 为 20, 但是 通过 设置 taco. orders. pageSize 属性, 我们 可以 很容易 地 将其 修改 为 任意 的 值。并且不用重启项目。
5. 2. 1 定义 配置 属性 的 持有 者
这里 并没有 说@ ConfigurationProperties 只能 用到 控制器 或 特定 类型 的 bean 中。
@ ConfigurationProperties 实际上 通常 会 放到 一种 特定 类型 的 bean 中, 这种 bean 的 目的 就是 持有 配置 数据。 这样 的 话, 特定 的 配置 细节 就能 从 控制器 和 其他 应用 程序 类 中 抽 离 出来, 多个 bean 也能 更容易 地 共享 一些 通用 的 配置。
配置 属性 持有 者 并没有 什么 特别 之处。 它们 只是 专门存放 Spring 环境 注入 到 其 属性 中的 bean。
5. 2. 2 声明 配置 属性 元 数据
元 数据 :IDE中 在配置文件中,鼠标悬停后可以得知相关信息
我觉得没什么用就不记笔记了。
5. 3 使用 profile 进行 配置
当 应用 部署 到 不同 的 运行时 环境 中的 时候, 有些 配置 细节 通常 会有 些 差别。配置 不同 环境 之间 有 差异 的 属性 时,
有种 办法 就是 使用 环境 变量, 通过 这种 方式 来 指定 配置 属性, 而 不 是在 application. properties 和 application. yml 中进 行 定义。
尽管 这种 方式 可以 运行, 但是 如果 配置 属性 比较 多, 那么 将它 们 声明 为 环境 变量 会 非常 麻烦。
相对于 这种 方式, 更推荐 采用 Spring profile。 profile 是一 种 条件 化 的 配置, 在 运行时, 根据 哪些 profile 处于 激活 状态, 可以 使用 或 忽略 不同 的 bean、 配置 类 和 配置 属性。
5. 3. 1 定义 特定 profile 的 属性
定义 特定 profile 相关 的 属性 的 一种 方式 就是 创建 另外 一个 YAML 或 属性 文件, 其中 只 包含 用于 生产 环境 的 属性。 文件 的 名称 要 遵守 如下 的 约定: application-{ profile 名}. yml 或 application-{ profile 名}. properties。 然后, 我们 就可以 在这里 声明 适用于 该 profile 的 配置 属性 了。
例如, 我们 可以 创建 一个 新的 名为 application- prod. yml 的 文件, 其中 包含 如下 属性:
spring:
datasource:
url: jdbc: mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN
定义 特定 profile 相关 的 属性 的 另外 一种 方式 仅 适用于 YAML 配置。 它 会 将 特定 profile 的 属性 和 非profile 的 属性 都 放到 application. yml 中, 它们 之间 使用 3 个中 划线 进行 分割, 并且 使用 spring. profiles 属性 来 命名 profile。 如果 按照 这种 方式 定义 生产 环境 的 属性, 等价 的 application. yml 如下 所示:
taco:
orders:
pageSize: 10
---
spring:
datasource:
url: jdbc: mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN
我们 可以 看到, application. yml 文件 通过 一组 中 划线(---) 分成 了 两部分。 第二 部分 指定 了 spring. profiles 值,
通过 创建 模式 为 application-{ profile 名}. yml 或 application-{ profile 名}. properties 的 YAML 或 属性 文件, 我们 可以 按 需 定义 任意 数量 的 profile。
或者, 我们 也可 以在 application. yml 中 再输入 3 个中 划线, 结合 spring. profiles 属性 来 指定 其他 名称 的 profile, 然后 添加 该 profile 特定 的 相关 属性。
5. 3. 2 激活 profile
在 application. yml 中,就可以 激活 profile
spring:
profiles:
active:
- prod
但是, 这 可能 是 激活 profile 最 糟糕 的 一种 方式。 这个 profile 会 变成 默认 的 profile, 我们 体验 不到 使用 profile 将 生产 环境 相关 属性 和 开发 环境 相关 的 属性 分开 的 任何 好处。
因此, 推荐 使用 环境 变量 来 设置 处于 激活 状态 的 profile。
在 生产 环境 中, 我们 可以 这样 设置
SPRING_ PROFILES_ ACTIVE:
% export SPRING_ PROFILES_ ACTIVE= prod
这样 部署 到 该 机器 上 的 任何 应用 就都 会 激活 prod profile, 对应 的 属性 会 比 默认 profile 具备 更高 的 优先级。
如果 以 可执行 JAR 文件 的 形式 运行 应用, 那么 我们 还可以 以 命令行 参数 的 形式 设置 激活 的 profile:
% java -jar taco-cloud.jar --spring.profiles.active=prod
你 可能 已经 注意 到了, spring. profiles. active 属性 名 是 复数 形式 的 profile。 这 意味着 我们 可以 设置 多个 激活 的 profile。 如果 使用 环境 变量, 通常 这可 以 通过 逗号 分隔 的 列表 来 实现:
% export SPRING_ PROFILES_ ACTIVE = prod, audit, ha
或者
spring:
profiles:
active:
- prod
- audit
- ha
5. 3. 3 使用 profile 条件 化 地 创建 bean
假设 我们 希望 某些 bean 仅在 特定 profile 激活 的 情况下 才 需要 创建。 在 这种 情况下,@ Profile 注解 可以 将 某些 bean 设置 为 仅 适用于 给定 的 profile。
@Bean
@Profile("dev")
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
//……
}
如果是多个profile 环境下都要启动,则改成
@Profile({"dev","qa"})
如果 除了 prod 激活 时, CommandLineRunner bean 都 需要 创建,
@Profile("!prod")
我们 还可 以在 带有@ Configuration 注解 的 类 上 使用@ Profile。
@Profile({"!prod","!qa"})
@Configuration
public class DevelopmentConfig {
//……
}
在这里, CommandLineRunner bean( 包括 DevelopmentConfig 中 定义 的 其他 bean) 只有 在 prod 和 qa 均 没有 激活 的 情况下 才会 创建。
5. 4 小结
- Spring bean 可以 添加@ ConfigurationProperties 注解, 这样 就 能够 从 多个 属性 源 中选 取 一个 来 注入 它的 值。
- 配置 属性 可以 通过 命令行 参数、 环境 变量、 JVM 系统 属性、 属性 文件 或 YAML 文件 等 方式 进行 设置。
- 配置 属性 可以 用来 覆盖 自动 配置 相关 的 设置, 包括 指定 数据 源 URL 和 日志 级别。
- Spring profile 可以 与 属性 源 协同 使用, 从而 能够 基于 激活 的 profile 条件 化 地 设置 配置 属性。