读书笔记 -- 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"”

 

posted on 2023-06-28 11:24  bruce_he  阅读(83)  评论(0编辑  收藏  举报