读书笔记 -- Spring Boot3 核心技术 Chapter2 Spring Boot 配置管理
2.1 配置类
2.1.1 自定义配置类
- @SpringBootConfiguration:SpringBoot 的专用注解,与 @Configuration 等效;
- @Configuration:Spring 的原生注解;
通过 @SpringBootConfiguration 或 @Configuration 注解的配置文件来配置所有 Bean 及其他组件。
2.1.2 导入配置
// 1. 导入配置类 @SpringBootConfiguration @Import({Configuration1.class, Configuration2.class}) // 通过 @Import 导入两个配置类,或者通过 @ComponentScan() 扫描默认的类路径或指定的包路径 public class MainConfig { @Bean // 等价于 XML 配置文件中的 <bean ...> 标签 public RestTemplate restTemplate() { return new RestTemplate(); } } // 2. 导入 XML 配置 使用 @ImportResource
2.2 配置文件
2.2.1 application
// Spring Boot 的配置参数主要在 applicaton 中,具体看 StandardConfigDataLocationResolver.class static final String CONFIG_NAME_PROPERTY = "spring.config.name"; static final String[] DEFAULT_CONFIG_NAMES = new String[]{"application"};
从 源码可以看出:
1)默认的配置在 application 中;
2)或者可以通过 "spring.config.name" 在命令行指定,如: java -jar demo.jar --spring.config.name=app
2.2.2 bootstrap
bootstrap 配置文件:属于 Spring Cloud 环境,需要引入 Spring Cloud。
对比 application 配置文件,bootstrap 有如下特性:
- 由父 ApplicationContext 加载,比 application 优先加载;
- 参数不能被覆盖;
2.2.3 配置文件类型
配置文件类型可以有两类:
1).properties
server.port = 8080
server.servlet.contextPath = /javastack
2).yml
// yml 格式:1)":" 后面需要一个空格;2)每层缩进2个空格 server: port: 8081 servlet: contextPath: /javastack
注意点:
- 1)@PropertySource 可注解 .properties 导入配置,不能注解 .yml;
- 2)@ConfigurationProperties 注解可支持两种格式;
- 3).yml :通过 YamlPropertiesFactoryBean 类被加载为一个 Properties 参数类,通过 YamlMapFactoryBean 被加载为 Map,通过 YamlPropertySourceLoader 被加载为 Spring 环境的 PropertySource;
2.3 配置绑定
2.3.1 Spring 中的配置绑定
通过@Value("${property}") 注解在成员变量上,或 通过 @PropertySource + @Value 注解。但这种注解方式非常不方便。
// 用例: \spring-boot-properties \Application \props\DbProperties \resources\config\db-config.properties // DbProperties.java /** * 1. @Data 注解: * 1)、@Data注解是lombok.jar包下的注解,该注解通常用在实体bean上,不需要写出set和get方法,但是具备实体bean所具备的方法,简化编程提高编程速度。 * 2)、@Data相当于@Getter、@Setter、@RequiredArgsConstructor、@ToString、@EqualsAndHashCode 这5个注解的合集。 * 2. @Component 注解: * - @Component是一个元注解,意思是可以注解其他类注解,如@Controller @Service @Repository @Aspect。官方的原话是: * 带此注解的类看为组件,当使用基于注解的配置和类路径扫描的时候,这些类就会被实例化。其他类级别的注解也可以被认定为是一种特殊类型的组件,比如@Repository @Aspect。所以,@Component可以注解其他类注解。 * -- @Component 和 @Bean 的区别,可参考:https://www.jianshu.com/p/3fbfbb843b63 */ @Data @Component @PropertySource(value = {"config/db-config.properties"}) // @PropertySource 注解不支持主流的 .yml 配置文件绑定,自身也需要结合 @Value 注解,故该用法不推荐 public class DbProperties { @Value("${db.username}") private String username; @Value("${db.password}") private String password; }
2.3.2 参数绑定
- 通过 JavaBean 提供的 setter 方法进行配置参数与 Java Bean 字段绑定, 然后 通过@ConfigurationProperties 注解将配置参数映射到一个 Java Bean 上;
- 启动类上,使用 @ConfigurationProperties 和 @EnableConfigurationProperties 注解;
// 用例: \spring-boot-properties \Application \props\JavastackProperties \resources\application.yml // 以 javastack(与下面的 prefix 相适应)配置参数// JavastackProperties.java @Data @Validated @ConfigurationProperties(prefix = "javastack") // 通过@ConfigurationProperties 注解将配置参数映射到一个 Java Bean 上,该注解的 prefix 或 value 参数用于指定映射的参数前缀 public class JavastackProperties { private boolean enabled; @NotNull private String name; private String site; private String author; private List<String> users; private Map<String, String> params; private Security security; } @Data class Security { private String securityKey; private String securityCode; }
2.3.3 构造器绑定
- 通过 @ConfigurationProperties 注解指定要绑定的配置参数的前缀,再使用 @ConstructorBinding 注解指定要绑定的构造器方法;
- 启动类上,使用 @ConfigurationProperties 和 @EnableConfigurationProperties 注解;
// 用例: \spring-boot-properties \Application \props\MemberProperties \resources\application.yml // 以 member(与下面的 prefix 相适应)配置参数 // MemberProperties.java @Data @NoArgsConstructor @ConfigurationProperties(prefix = "member") // 此时,@ConfigurationProperties 注解在类上 public class MemberProperties { private String name; private int sex; private int age; private String country; private Date birthday; public MemberProperties(String name, int sex, int age) { this.name = name; this.sex = sex; this.age = age; } @ConstructorBinding // 如果有多个构造器,则需要用 @ConstuctorBinding 注解指定要使用的构造器 public MemberProperties(String name, int sex, int age, @DefaultValue("China") String country, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { this.name = name; this.sex = sex; this.age = age; this.country = country; this.birthday = birthday; } }
2.3.4 Bean 配置绑定
- 通过 @ConfigurationProperties 和 @Bean 注解在方法上。但,该方式只能绑定简单的数据类型,无法处理像 Date 这种数据类型。
- 因为使用了 @Bean 注解,所以不需要 @EnableConfigurationProperties 注解(在 Applicaton.java 中 @EnableConfigurationProperties 就不需要 import MainConfig.class了);
// 用例: \spring-boot-properties \Application \props\OtherMember \config\MainConfig \resources\application.yml // 以 member(与下面的 prefix 相适应)配置参数 // OtherMember.java @Data @NoArgsConstructor public class OtherMember { private String name; private int sex; private int age; } // MainConfig.java @SpringBootConfiguration public class MainConfig { @Bean @ConfigurationProperties(prefix = "member") public OtherMember otherMember() { return new OtherMember(); } }
2.3.5 参数类扫描
- 参数绑定(2.3.2)、构造器绑定(2.3.3)需要 @ConfigurationProperties 和 @EnableConfigurationProperties 注解使用。但是 @EnableConfigurationProperties 需要列出所有的参数类;
- 相比下,可以使用 @ConfigurationPropertiesScan 注解代替 @EnableConfigurationProperties 扫描所有包目录下的参数类;
@SpringBootApplication @RequiredArgsConstructor @ConfigurationPropertiesScan // 使用了 @ConfigurationPropertiesScan 代替了下面的 @EnableConfigurationProperties //@EnableConfigurationProperties(value = {JavastackProperties.class, MemberProperties.class}) @Slf4j public class Application {
2.3.6 配置验证
@ConfigurationProperties 注解可以有效进行参数验证,如在字段上使用 @NotNull 注解
// JavastackProperties.java @Data @Validated // 验证 @NotNull,同时需要依赖 @ConfigurationProperties(prefix = "javastack") public class JavastackProperties { private boolean enabled; @NotNull private String name; ... } // pom.xml 验证的依赖 <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
2.4 外部配置
2.4.1 配置源
Spring Boot 的配置源:放在应用配置文件中,外部配置(.properties 文件、.yml 文件、环境变量、命令行参数)
2.4.2 配置优先级
配置源的优先级由低到高:
- 1)默认参数(SpringApplication.setDefaultProperties);
- 2)使用 @PropertySource 注解绑定的配置;
- 3)应用配置文件中的参数(application);
- 4)配置了 random.* 随机数的参数;
- 5)系统环境变量;
- 6)Java System Properties;
- 7)java:comp/env 的 JNDI 参数;
- 8)ServletContext 初始化参数;
- 9)ServletConfig 初始化参数;
- 10)来自 SPRING_APPLICATION_JSON 的参数;
- 11)命令行参数;
- 12)单元测试上的参数;
- 13)使用 @TestPropertySource 注解绑定的参数;
- 14)DevTools 全局设置参数(来自 $HOME/.config/spring-boot);
多个应用配置文件(application)由低到高的优先级:
- 1)应用配置文件(jar 包内);
- 2)指定了 profile 的配置文件,如 application-dev.properties(jar 包内);
- 3)应用配置文件(jar 包外);
- 4)指定了 profile 的配置文件,如 application-dev.properties(jar 包外);
** 如果多个配置源的参数值相同,则高优先级会覆盖低优先级的值。
** 同一位置如有 .properties 和 .yml 两个格式文件,则优先使用 .properties,所以,建议使用统一格式。
2.4.3 命令行参数
// 使用 -- 在命令行添加参数 java -jar demo.jar --server.port = 8081
// 默认,Spring Boot 会将命令行参数添加到 Spring 环境,可设置成不添加 public static void main(String[] args) { SpringApplication.setAddCommandLineProperties(false); springApplication.run(Application.class, args); }
2.5 导入配置
// application.yml spring: config: import: - optional:classpath:/config/app.yml // app.yml javastack: enabled: true site: app.javastack.cn
这里,导入的外部的配置文件(app.yml)的优先级要高于导入的配置文件(application.yml)的优先级。
2.7 多文档配置
2.7.1 配置格式
格式:.yml 文件通过 “---”分割,.properties 文件通过“#---”分割
注意点:
- 1)分隔符的前面不能有空格,并且分隔符要连续;
- 2)不能被 @PropertySource 和 TestPropertySource 注释加载;
2.7.2 激活多文档配置
多文档配置需要配合指定的参数激活:
激活参数 | 说明 |
spring.config.active.on-profile | 根据指定的 Profile 激活,即该参数仅限于指定的 Profile |
spring.config.active.on-cloud-platform | 根据指定的云平台激活 |
// application.yml,现在可以激活 dev,相关参数值覆盖了默认的参数值 spring: profiles: active: dev --- member: name: Jack sex: 1 age: 20 birthday: 2000-01-01 21:00:00 spring: config: activate:
# 即,上面的参数仅限于 dev 或 test 的 Profile on-profile: "dev | test"
2.8 Profile
2.8.2 激活 profile
// 方式1:通过 .yml 文件 spring: profiles: active: dev // 方式2:启动方式上激活: public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Application.class); springApplication.setAdditionalProfiles("dev"); springApplication.run(args); } // 方式3: @Profile 注解配置类 + .yml 文件 active 激活 // 配置类 MainConfig.java @Profile("main") @SpringBootConfiguration @Import({Configuration1.class, Configuration2.class}) public class MainConfig { @Bean @ConfigurationProperties(prefix = "member") public OtherMember otherMember() { return new OtherMember(); } } // application.yml spring: profiles: active: test, main
@Profile 注解的使用说明:
- 1)如果是 @ConfigurationPropertiesScan 注解,则 @Profile 可直接注解在 @ConfigurationProperties 类上;
- 2)如果是 @EnableConfigurationProperties 注解,则 @Profile 注解在 @EnableConfigurationProperties 配置类上;
2.8.3 切换 Profile
// 可以被命令行替代,如果命令行使用 "java -jar demo.jar --sping.profiles.active=test" 将会被替换成 test 环境 spring: profiles: # active:可以被命令行切换 active: dev, main // 不能被命令行替代 spring: profiles: # include:不可以被命令行切换 include: - dev - main
2.8.4 Profile 分组
spring: profiles: default: dev # active: 可以被命令行切换 (java -jar xx.jar --spring.profiles.active=test 将会覆盖此处设置的 profile) # 注释掉 include,如果激活多个 profile,则后一个会覆盖前一个 test, dev, prod, main active: dev, main # include 不会被切换 include: - dev - main # group: 只要 main 环境激活, main1 和 main2 才会一起被激活 (注释 active 和 include 里面的 main,则 main1 和 main2 不会激活) group: main: - main1 - main2 --- member: name: Jack1 spring: config: activate: on-profile: main1 --- member: name: Jack2 spring: config: activate: on-profile: main2
2.8.5 指定 Profile 配置文件
添加如下配置文件: application.yml、application-dev.yml、application-test.yml、application-prod.yml、application-main.yml,然后逐渐 active,通过最终输出,发现其优先级由低到高的顺序:
default -> test -> dev -> prod -> main(?不知道对不对)
spring:
profiles:
# active:可以被命令行切换
active: test, dev, prod, main
2.8.6 使用限制
参数 | 功能 | 说明 |
spring.profiles.default | 指定默认的 Profile | 当不指定 Profile 时,默认生效的 Profile |
spring.profiles.active | 激活指定的 Profile | 可以被高优先级的配置源替换 |
spring.profiles.include | 激活指定要包含的 Profile | 不会被其他配置源替换 |
spring.profiles.group | 指定 Profile 分组 | 激活一个分组,就会激活组下所有 Profile,需要先激活 group 的 Profile,如上面例子,需要先激活 main |
** 以上参数不能用于多文档配置中或指定 Profile 配置中(如 application-main.yml)
Profile 的实际应用:
application.properties中配置通用内容,并设置spring.profiles.active=dev,以开发环境为默认配置
application-{profile}.properties中配置各个环境不同的内容
通过命令行方式去激活不同环境的配置。
具体链接:https://blog.csdn.net/bigtree_3721/article/details/82385570
2.9 加载机制
可参照链接(依据 Java 17,更新了内容) https://www.cnblogs.com/bruce-he/p/17565796.html,原文链接 https://blog.csdn.net/Y_hanxiong/article/details/105207308
2.10 配置加密
2.10.1 概述
Spring Boot 应用配置的加/解密 有如下方案:
- 1)使用配置中心(支持自动加/解密);
- 2)使用数据库机制;
- 3)使用自定义加/解密机制;
- 4)使用 Jasypt Spring Boot(第三方加/解密方案);
2.10.2 使用配置中心(支持自动加/解密)
支持自动解密的中间件,就可以将所有的动态配置和敏感信息都存储在配置中心,如 Spring Cloud 微服务生态中的 Spring Cloud Config 配置中心。
// 以 {cipher} 开头,并以 '' 括起来 spring: datasource: username: '{cipher}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
2.10.3 使用数据库机制
数据加密存储到数据库,Spring Boot 启动时,解密加载到内存。可应用于 关系型数据库(MySql,Oracle)、内存数据库(Redis、ZooKeeper)等;
2.10.4 使用自定义加/解密机制
如果仅极少的敏感配置,可使用系统中现有的对称加密算法,再自定义一个数据源类实现自定义的加/解密机制。
@Bean public DataSource dataSource() { DataSource dataSource = new DruidDataSource(); // 解密 String username = this.getUsername(); if (username.startWith('{cipher}') { username = Encrypt.decrypt(username, this.getKey()); } dataSource.setUsername(username); ... return dataSource; }
2.10.5 Jasypt Spring Boot
// JasyptTest.java @Slf4j @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor stringEncryptor; @Test public void encrypt() { String usernameEnc = stringEncryptor.encrypt("javastack"); String passwordEnc = stringEncryptor.encrypt("javastack.cn"); // 加密的是随机 log.info("test username encrypt is {}", usernameEnc); log.info("test password encrypt is {}", passwordEnc); log.info("test username is {}", stringEncryptor.decrypt(usernameEnc)); log.info("test password is {}", stringEncryptor.decrypt(passwordEnc)); } } // application.yml
# application.yml 文件中添加 jasypt 表示将配置自动解密 jasypt: encryptor: # password 关键字是必须的 password: G9w0BAQEFAASCBKYwggSiAgEAAoIBAQC // pom.xml <artifactId>spring-boot-jasypt</artifactId> <properties> <jasypt-spring-boot.version>3.0.5</jasypt-spring-boot.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>${jasypt-spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
或将加密信息存在 IDE 中,“-Djasypt.encryptor.password="G9w0BAQEFAASCBKYwggSiAgEAAoIBAQC"”
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)