1. spring boot 配置

1.1 配置文件

  • application.properties 和 application.yml(or .yaml);

    • yaml 和 properties 配置文件允许同时存在,但 properties 配置文件的内容会覆盖 yaml 配置文件的内容;
    • yaml 默认 UTF-8 编码,properties 默认 ASCII 编码,使用 properties 配置需在 setting 中设置格式转换;
    • yaml 和 properties 在线格式转换工具

      1.1.1 配置文件值注入

      (1) 案例说明
  • 第一步:jar 包准备(导入配置文件处理器,配置文件进行绑定就会有提示)

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-configuration-processor</artifactId>
    4. <optional>true</optional>
    5. </dependency>
  • 第二步:准备一个存储着不同类型字段的 bean 对象

    1. /**
    2. * 将配置文件中的每一个属性的值映射到这个组件中
    3. * @ConfigurationProperties 注解的功能是将这个类中的所有属性和配置文件中相关的配置进行绑定
    4. * 只有这个组件是容器中的组件,才能使用容器提供的 @ConfigurationProperties 功能
    5. */
    6. @Component
    7. @ConfigurationProperties(prefix = "person")
    8. public class Person {
    9. private String name;
    10. private Integer age;
    11. private Boolean male;
    12. private Date birth;
    13. private Map<String, Object> maps;
    14. private List<Object> lists;
    15. private Dog dog;
    16. //.... 此处省略 getter/setter 方法及 toString 方法
    17. }
    18. // 注:此处如果不注解说明 Dog 也是容器中的组件,在配置文件中的就算设定值,打印的结果也为 null
    19. @Component
    20. public class Dog {
    21. private String name;
    22. private Integer age;
    23. //.... 此处省略 getter/setter 方法及 toString 方法
    24. }
  • 第三步:在 application.yml 中对 bean 对象进行配置

    1. person:
    2. name: 陈玉婷
    3. age: 25
    4. male: false
    5. birth: 1994/10/31
    6. maps: {k1: v1, hh: 12}
    7. lists: [apple, peach, banana]
    8. dog: {name: 豆豆, age: 2}
  • 第四步:进行 spring-boot 单元测试,打印经过 yml 配置的 bean 对象

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringBoot02ConfigApplicationTests {
      @Autowired
      Person person;
    
      @Test
      public void contextLoads() {
          System.out.println(person);
          //输出结果:Person{name='陈玉婷', age=25, male=false, birth=Mon Oct 31 00:00:00 CST 1994, maps={k1=v1, hh=12}, lists=[apple, peach, banana], dog=Dog{name='豆豆', age=2}}
      }
    }
    

    (2) @ConfigurationProperties() 和 @Value() 的区别

  • @ConfigurationProperties() 为 spring boot 注解,@Value() 为 spring 原生注解;

  • 两种方法都可以实现 bean 对象的值注入,当两个注解都使用时,@ConfigurationProperties() 会覆盖 @Value() 的内容;
  • 如何使用
    • 若只在某个业务逻辑中需要使用配置文件中的某项值,则用 @Value
    • 若专门编写一个 javaBean 来和配置文件进行映射,则用 @ConfigurationProperties;
  • 区别

    • 两者的使用位置区别

      @Component
      @ConfigurationProperties(prefix = "person")
      public class Person {
      @Value("${person.name}")//从配置文件中获取值
      private String name;
      @Value("#{12*2}")//指定值
      }
      
    • 两者的应用场景和支持功能区别 | | @ConfigurationProperties | @Value | | :—-: | —- | —- | | 功能 | 批量注入配置文件中的属性 | 一个个指定 | | 松散绑定(松散语法) | 支持,eg:last-name | 不支持 | | SpEL | 不支持 | 支持,eg:#{11*2} | | JSR303数据校验 | 支持,eg:@Email、@Max | 不支持 | | 复杂类型(map、list)等封装 | 支持 | 不支持 |

(3) @PropertySource 和 @ImportSource

  • @PropertySource 是从配置文件中加载数据,@ImportSource 是将配置文件放到 ApplicationContext 中;

(1)@PropertySource 用来加载指定的配置文件,默认加载 application.properties 或者 application.yml;

  • 案例说明

    • @PropertySource(value = {“classpath:person.properties”}) 相当于@PropertySource(value = {“classpath:person.properties”,”classpath:application.properties”});
    • 在不存在 application.yml 对 bean 对象同样值注入的情况下,以下案例中由于两个配置文件中 prefix 都是 person,所以最终可实现完整的值注入;
    • prefix 只能是单值,所以以后如果要在多个配置文件中对同一个 bean 对象实现不同值注入的话,要保证对 bean 对象的引用名一致;
      @PropertySource(value = {"classpath:person.properties"})
      @ConfigurationProperties(prefix = "person")
      @Component
      public class Person {
      private String name;
      private Integer age;
      private Boolean male;
      private Date birth;
      private Map<String, Object> maps;
      private List<Object> lists;
      private Dog dog;
      }
      //person.properties 文件配置
      person.name='陈玉婷111'
      person.age=25
      person.male=false
      person.birth=1994/10/31
      person.maps.k1=v1
      person.maps.hh=12
      person.lists[0]=apple
      person.lists[1]=peach
      person.lists[2]=banana
      //application.properties 文件配置
      person.dog.name='豆豆'
      person.dog.age=2
      
      (2)@ImportResource 用于导入 Spring 的配置文件(applicationContext.xml 等);
  • @ImportResource 是将 .xml 配置环境导入根上下文的方法,在一般开发中推荐使用注解实现;

    • @Configuration 不仅说明当前类是等同于 bean,xml 的配置类,而且自动将其导入根上下文中;
    • @Bean 用于表示当前方法的返回结果为一个 bean,等同于 标签;
      @Configuration
      public class myAppConfig {
      //如果此处不定义 bean 的 value 值,则从容器中获取该 bean 的名称为方法名“getHelloService”;
      @Bean("helloService")
      public HelloService getHelloService(){
         System.out.println("在容器中创建了 helloService 的 bean");
         return new HelloService();
      }
      }
      
  • 案例说明

    • 准备:bean.xml 中加入一个 bean 对象

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <bean id="helloService" class="com.cyt.springboot.service.HelloService">
      </bean>
      </beans>
      
    • 在 spring-boot 的根上下文环境中引入某个 bean 所在容器的地址 ```java @ImportResource(value = {“classpath:bean.xml”}) @SpringBootApplication public class SpringBoot02ConfigApplication { public static void main(String[] args) { SpringApplication.run(SpringBoot02ConfigApplication.class, args); }

}


   - 测试
```java
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot02ConfigApplicationTests {
    @Autowired
    ApplicationContext ac;

    @Test
    public void testBean(){
        Boolean haveBean = ac.containsBean("helloService");
        System.out.println(haveBean);
    }
}

1.1.2 配置文件占位符

  • 随机数

    ${random.value}
    ${random.int}
    ${random.long}
    ${random.int(10)}//在0~10范围内的随机数
    ${random.int(10,20)} //在指定范围内的随机数
    
  • 引用在配置文件中配置的其他属性的值

    • 如果该属性值没有在配置文件中配置,可以使用:来指定默认值;
    • 如果该属性值没有在配置文件中配置,而且也没指定默认值,则会原样输出,如 ${person.name}_豆豆;
  • 案例 ```java //在 application.properties 中进行如何配置 person.name=${random.uuid} person.age=${random.int} person.male=false person.birth=1994/10/31 person.maps.k1=${random.value} person.maps.hh=12 person.lists[0]=apple person.lists[1]=peach person.lists[2]=banana person.dog.name=${person.name:陈玉婷}_豆豆 person.dog.age=${random.int(3,5)}

//在 sprong boot 单元测试中打印 person Person{name=’10b0afbe-a739-4777-a4f3-2c1254a0f305’, age=-1975590516, male=false, birth=Mon Oct 31 00:00:00 CST 1994, maps={k1=ae5fd048b26a3384921e20d89274e53f, hh=12}, lists=[apple, peach, banana], dog=Dog{name=’c553d64b-2679-48dd-991e-a7a1f11c4ea7_豆豆’, age=4}}

<a name="dFm0u"></a>
### 1.1.3 Profile 不同环境的配置激活

- Profile 是 Spring 对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境;
   - 程序启动时会默认加载主配置文件 application.propertie/yml 的启动端口;
   - 如果想使用指定环境下的端口,需在配置文件或程序运行参数中进行激活;
- 针对两种类型配置文件的不同使用方式
   - properties 的多 profile 文件方式
      - 文件名格式:application-{profile}.properties/yml,例如:application-dev.properties、application-prod.properties 等;
      - 在 application.properties 中指定激活哪个配置文件:spring.profiles.active = dev;

![mvc1.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584326628903-be312ad7-81ce-4759-b29c-38f787bebfde.png#align=left&display=inline&height=523&name=mvc1.png&originHeight=523&originWidth=1282&size=52174&status=done&style=none&width=1282)

   - yml 的多文档块方式
      - 书写格式:在 application.yml 中每个文档块使用 --- 分割;
      - 在 application.yml 中指定激活哪个配置文件:"spring.profiles.active = prod" 的 yml 写法;

![image.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584327472586-faaefafc-4af1-4b98-9d22-b0d411dc4f17.png#align=left&display=inline&height=617&name=image.png&originHeight=617&originWidth=1306&size=100885&status=done&style=none&width=1306)

- 激活指定 profile 的三种方式
   - 在配置文件中指定 spring.profiles.active=dev(如上)
   - 参数指定:**参数指定要优先于配置文件指定**
      - 主程序输入参数(或项目打包后命令行输入参数):[java -jar xxx.jar] --spring.profiles.active=dev;
      - 虚拟机参数:-Dspring.profiles.active=dev

![mvc1.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584328750048-3511ef5b-9752-4fdb-9c18-40c2109b6f39.png#align=left&display=inline&height=787&name=mvc1.png&originHeight=787&originWidth=1313&size=71354&status=done&style=none&width=1313)
<a name="wqYjg"></a>
### 1.1.4 配置文件加载位置及顺序

- springboot 启动时会扫描以下位置的主配置文件 application.propertie/yml 来作为其默认配置文件;
- 以下是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级的配置内容会覆盖低优先级的配置内容,SpringBoot 最终会从这四个位置合成、互补成完整的配置内容;
- 小结
   - 根路径加载优先级要高于类路径,且在特定路径下 config 目录下的配置文件优先级更高一点;
   - 如果在其中某一个位置出现指定环境激活 spring.profiles.active=dev,则最终的配置为激活配置;
   - 如果在多个位置出现指定环境激活,则同样按照优先级实现激活配置的覆盖;

//项目根路径 file: ./config/ file: ./ //项目类路径 classpath: /config/ classpath: /


- 其他
   - 可以使用 **spring.config.location** 来改变默认的配置文件位置,利用命令行参数的形式在启动项目时指定配置文件的新位置,指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;

![image.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584331502096-f97831ec-9b37-44c2-980c-83ee76a089dc.png#align=left&display=inline&height=646&name=image.png&originHeight=646&originWidth=1303&size=108765&status=done&style=none&width=1303)
<a name="Shpkx"></a>
### 1.1.5 外部配置加载顺序<br />

- SpringBoot 也可以从以下位置(常用方法)加载配置(优先级从高到低),同样遵循高优先级的配置会覆盖低优先级的配置,所有的配置会形成互补配置;
- 所有可支持的配置来源参考 [官方完整介绍](https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#boot-features-external-config)
  1. 命令行参数 格式:—配置项=值,多个配置用空格分开
  2. 来自java:comp/env的JNDI属性
  3. Java系统属性(System.getProperties())
  4. 操作系统环境变量
  5. RandomValuePropertySource 配置的 random.* 属性值
  6. jar包外部的 application.properties 或 application.yml(不带spring.profile) 配置文件
  7. jar包内部的 application.properties 或 application.yml(不带spring.profile) 配置文件
  8. jar包外部的 application-{profile}.properties 或 application.yml(带spring.profile) 配置文件 ⤴️
  9. jar包内部的 application-{profile}.properties 或 application.yml(带spring.profile) 配置文件 ⤴️
  10. @Configuration 注解类上的 @PropertySource
  11. 通过 SpringApplication.setDefaultProperties 指定的默认属性 ```
  • 小结

    • 所有的配置都可以在命令行上进行指定

      • 注:此处记录实现过程中的一个异常,在 IntelliJ IDEA 2019.3 community,jdk1.8.0_131,spring boot 2.2.1 环境下,使用 —server.context-path=/boot01 并不能修改路径,反而会报 404 错误;
        java -jar xxx.jar --server.port=8087  --server.context-path=/boot01
        
    • 从 jar 包读取配置文件,遵循无 profile 的先外到内,然后是有 profile 的先外到内;

      1.2 自动配置

      1.2.1 自动配置的原理

  • SpringBoot 启动的时候加载主配置类,开启了自动配置功能;

    @SpringBootApplication
    public class SpringBoot02ConfigApplication {
      public static void main(String[] args) {
          SpringApplication.run(SpringBoot02ConfigApplication.class, args);
      }
    }
    
  • @SpringBootApplication 会调用 loadSpringFactories() 方法将 jar 包类路径下 META-INF/spring.factories 中扫描到的文件包装成 properties 对象,从 properties 对象中获取到 EnalbeAutoConfiguration.class(类名)对应的值,然后把他们添加到容器中;

mvc1.png
image.png

1.2.2 案例分析

  • 以 HttpEncodingAutoConfiguration 为例,说明如何在 properties 中给配置文件的属性进行赋值; ```java package org.springframework.boot.autoconfigure.web.servlet;

……

@Configuration( proxyBeanMethods = false)//表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件

/**

  • 1.启动指定类的 ConfigurationProperties 功能;
  • 2.将配置文件中对应的值和 HttpProperties 绑定起来;
  • 3.把 HttpProperties 加入到 ioc 容器中; */ @EnableConfigurationProperties({HttpProperties.class}) @ConditionalOnWebApplication( type = Type.SERVLET)//判断当前应用是否是web应用,如果是,当前配置类生效 @ConditionalOnClass({CharacterEncodingFilter.class})//判断当前项目有没有这个类 @ConditionalOnProperty( prefix = “spring.http.encoding”, value = {“enabled”}, matchIfMissing = true )//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的,即使我们配置文件中不配置 pring.http.encoding.enabled=true,也是默认生效的; public class HttpEncodingAutoConfiguration { private final Encoding properties; //已经和 SpringBoot 的配置文件映射了

    //只有一个有参构造器的情况下,参数的值就会从容器中拿 public HttpEncodingAutoConfiguration(HttpProperties properties) {

     this.properties = properties.getEncoding();
    

    }

    @Bean //给容器中添加一个组件,这个组件的某些值需要从 properties 中获取 @ConditionalOnMissingBean //判断容器有没有这个组件?(容器中没有才会添加这个组件) public CharacterEncodingFilter characterEncodingFilter() {

     CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
     filter.setEncoding(this.properties.getCharset().name());
     filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
     filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
     return filter;
    

    } …… ```

  • 自动配置类的类名格式一般为 XXXAutoConfiguration,自动配置类中的组件对应的可配置属性一般放在

xxxProperties 类中封装着;

@EnableConfigurationProperties({HttpProperties.class})
@ConfigurationProperties(
    prefix = "spring.http"
)
public class HttpProperties {
    private boolean logRequestDetails;
    private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();
    ...
}

1.2.3 如何基于 spring boot 已有配置进行开发

  • 在开发中,spring boot 配置方法总结
    • SpringBoot 启动时会加载大量的自动配置类;
    • 首先在 META-INF/spring.factories 中找到 EnalbeAutoConfiguration 下的所有自动配置类,再来看这个自动配置类中到底配置了哪些组件,如果已包含自己想要的配置功能则无需再配置;
    • 如果 spring.factories 没有想要实现的配置功能,就自定义一个配置类,并利用 @Configuration 和 @ConfigurationProperties(prefix = “”) 等实现将自定义的配置类导入 ioc 容器及在 properties 为容器中的 bean 对象进行值的注入;
    • 如果要想在 application.properties/yml 中为自动配置类的某些属性重新赋值,可找到该自动配置类(类名格式一般为 XXXAutoConfiguration),然后在类上的注解中找到 @EnableConfigurationProperties(),该注解的属性所对应的 xxxProperties 类封装着可通过 properties 配置的属性;
  • 如何查看哪些自动配置类生效
    • 自动配置类的作用
      • 自动配置类生效后会自动向容器中添加各种组件,这些组件的属性可从对应的 properties 类中获取,而这些 properties 类中的每一个属性和 application 配置文件绑定;
    • 自动配置类如何生效:只有在满足当前不同的条件判断 @ConditionXXX 才会生效;
    • 如何查看已生效的自动配置类
      • 通过配置文件 application.properties 启用 debug=true 属性,来让控制台打印自动配置报告;
        • Positive matches :(自动配置类启用的),Negative matches:(没有启动,没有匹配成功的自动配置类);