乐趣区

关于springboot:Spring-Boot-第二弹配置文件详解史上最全

往期文章回顾:Spring Boot 第一弹,问候一下世界!!!

前言

 上弹说到 如何应用 Spring Boot 问候一下世界 ,想必大家都对Spring Boot 曾经有肯定的把握了。如果还没看的,没关系,能够点击下面 往期回顾链接前去学习
 明天咱们一起来学习Spring Boot 第二弹,玩转Spring Boot 配置文件
 说起Spring Boot 的配置文件,真的是 爱恨交加 ,绝对于之前 Spring 大量的配置文件,当初的 Spring Boot 几乎几乎几乎。。。, 怎一个爽字了得 。当然了,爽的同时,也迎来了不少困扰,比方: 咱们对于 Spring Boot 是 如何实现 的只须要批改配置文件就能达到肯定成果也是充斥了好奇,这个就须要咱们去浏览 Spring Boot 的源码 - 主动拆卸原理 了,。这里我就不再赘述了, 前面我会出 专门针对源码进行剖析的文章,敬请期待吧!!!

话不多说,开搞!!!

Spring Boot 配置文件格式

Spring Boot 官网 提供了两种罕用的配置文件格式,别离是 propertiesYML 格局。相比于 properties 来说,YML更加年老,层级也是更加明显。强烈推荐应用 YML 格局

Spring Boot 配置文件优先级加载机制

Spring Boot 我的项目 启动会扫描以下地位的 application.properties 或者 application.yml 作为默认的配置文件.

  1. file:./config/
  2. file:./config/*/
  3. file:./
  4. classpath:/config/
  5. classpath:/

 加载的优先级程序是 从上向下加载 ,并且 所有的文件都会被加载 高优先级的内容 会笼罩 低优先级的内容,造成互补配置

徒手撕源码

 咱们能够从 ConfigFileApplicationListener 这个类中找到,其中 DEFAULT\_SEARCH\_LOCATIONS 属性设置了加载的目录:

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
    private static final String DEFAULT_NAMES = "application";
    private static final Set<String> NO_SEARCH_NAMES = Collections.singleton((Object)null);
    private static final Bindable<String[]> STRING_ARRAY = Bindable.of(String[].class);
    private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
    private static final Set<String> LOAD_FILTERED_PROPERTY;
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
                                          ...
                                          ...
                                          ...
}

 而后在 ConfigFileApplicationListener 类中的 getSearchLocations 办法中去逗号解析成 Set,其中 外部类 Loader 负责这一配置文件的加载过程,包含加载profile 指定环境的配置,以 application+’-’+name 格局的拼接加载。

外部类 Loader 的 load 办法

private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {this.getSearchLocations().forEach((location) -> {String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
        boolean isDirectory = location.endsWith("/");
        Set<String> names = isDirectory ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
        names.forEach((name) -> {this.load(nonOptionalLocation, name, profile, filterFactory, consumer);
        });
    });
}

getSearchLocations()办法

private Set<String> getSearchLocations() {Set<String> locations = this.getSearchLocations("spring.config.additional-location");
    if (this.environment.containsProperty("spring.config.location")) {locations.addAll(this.getSearchLocations("spring.config.location"));
    } else {locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/"));
    }

    return locations;
}

asResolvedSet()

private Set<String> asResolvedSet(String value, String fallback) {List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(value != null ? this.environment.resolvePlaceholders(value) : fallback)));
    Collections.reverse(list);
    return new LinkedHashSet(list);
}

 其实能够看出源码外面给出的 配置文件排列程序跟加载程序是相同的 。而是应用了Collections.reverse(list) 办法

  • 咱们也能够通过指定配置 spring.config.location 来扭转默认配置,个别在我的项目曾经打包后,咱们能够通过以下指令来 加载内部的配置
java -jar XXX-0.0.1-SNAPSHOT.jar --spring.config.location=F:/application.yml
  • 另外也能够通过 命令行参数进行配置

   所有的配置 都能够在 命令行 上进行指定
    多个配置用空格离开;-- 配置项 = 值

java -jar XXX-0.0.1-SNAPSHOT.jar 
--server.port=8888 --server.context-path=/qlh

上面给出优先级 从高到低 的配置文件排列程序:

  • 命令行参数
  • 来自 java:comp/env 的 JNDI 属性
  • Java 零碎属性(System.getProperties())
  • 操作系统环境变量
  • RandomValuePropertySource 配置的 random.* 属性值

备注:由 jar 包内向 jar 包内进行寻找,优先加载带 profile 的,再加载不带 profile 的

  • jar 包内部的 application-{profile}.properties 或 application.yml(带 spring.profile)配 - 置文件
  • jar 包外部的 application-{profile}.properties 或 application.yml(带 spring.profile)配置文件
  • jar 包内部的 application.properties 或 application.yml(不带 spring.profile)配置文件
  • jar 包外部的 application.properties 或 application.yml(不带 spring.profile)配置文件
  • @Configuration 注解类上的 @PropertySource
  • 通过 SpringApplication.setDefaultProperties 指定的默认属性

properties、YAML 配置优先级加载机制

Spring Boot应用一个以 application 命名的配置文件作为 默认的全局配置文件 。反对properties 后缀结尾 的配置文件或者 以 yml/yaml 后缀结尾 的 YAML 的文件配置。

以设置利用端口为例 初体验 Spring Boot 配置文件

properties 后缀结尾(application.properties)

server.port=80

yml/yaml 后缀结尾(application.yml/application.yaml)

server:
  prot: 8088

注:同一目录下,properties 配置文件优先级 > yml/yaml 配置文件优先级。因而在 jar 包启动时 如果 带上 properties 写法的配置 能够笼罩配置。
yaml/yml 配置文件写法冒号后要加空格

properties 配置文件

语法结构为:key=value

值类型:

数字,字符串,布尔,日期

person.name=tinygrey
person.age=18
person.status=true
person.birthday=2020/11/23

对象、Map

#Map
person.assets.phone=iphone 12
person.assets.car= 捷豹
#对象
person.dog.name= 奶狗
person.dog.age=3

数组

person.hobby[0]= 打篮球
person.hobby[1]= 睡觉
person.hobby[2]= 玩游戏
person.hobby[3]= 学习

yml/yaml 配置文件

 以空格的 缩进水平来管制层级关系 。空格的个数并不重要,只有 右边空格对齐则视为同一个层级 。留神 不能用 tab代替空格。且 大小写敏感 。反对 字面值,对象,数组 三种数据结构,也反对 复合 构造

字面值 字符串 布尔类型 数值 日期 。字符串默认不加引号,单引号会本义特殊字符。日期格局反对 yyyy/MM/dd HH:mm:ss
对象 :由 键值对 组成,形如 key:(空格)value 的数据组成。冒号前面的空格是必须要有的,每组键值对占用一行,且缩进的水平要统一,也能够应用 行内写法 {k1: v1, ....kn: vn}
数组 :由形如 -(空格)value 的数据组成。短横线前面的空格是必须要有的,每组数据占用一行,且缩进的水平要统一,也能够应用 行内写法:[1,2,...n]
复合构造 :下面三种数据结构 任意组合

值类型:

数字,字符串,布尔,日期

person:
  name: tinygrey
  age: 18
  status: true
  birthday: 2002/04/02

对象、Map

person:
  #Map
  assets:
    phone: iphone 12
    car: 捷豹
  #对象
  dog:
    name: 奶狗
    age: 3
# 行内写法
person:
  #Map
  assets: {phone: iphone 12,car: 捷豹}
  #对象
  dog: {name: 奶狗,age: 3}

数组

person:
  hobby:
    - 打篮球
    - 睡觉
    - 玩游戏
    - 学习
# 行内写法
person:
  hobby: [打篮球, 睡觉, 玩游戏, 学习]

注:YML 是一种旧式的格局 ,层级显明, 强烈推荐应用
留神如下::
字符串 能够不加引号,若 加双引号则输入特殊字符 ,若 不加或加单引号则本义特殊字符
数组类型 ,短横线前面要有空格; 对象类型 ,冒号前面要有空格
yaml/yml 是以空格缩进的水平来管制层级关系,但 不能用 tab 键代替空格 大小写敏感

本文所有提到的配置文件都对应上面实体类做为参考

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {@Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    private boolean status;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "dog")
class Dog {
    private String name;
    private Integer age;
}

配置文件如何跟实体类绑定

Spring Boot所有的配置都是为了取值,Spring Boot 提供了一些取值的形式。咱们一起来看一下。

@ConfigurationProperties(prefix = "person")详解

注: 该注解用于从配置文件中取值,反对简单的数据类型,然而不反对 SPEL 表达式
prefix 属性: 指定获配置的前缀,毕竟配置文件中的属性很多,也有很多重名的,必须用一个前缀来辨别下。
该注解能够 标注在类上 也能够 标注在办法上 ,这里就能够看出它有 两种获取值的形式

标注在类上

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component // 注入到 IOC 容器中
@ConfigurationProperties(prefix = "person")// 从配置文件中读取文件
public class Person {@Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}

标注在办法上

/**
 * @Bean 将返回的后果注入到 IOC 容器中
 * @ConfigurationProperties 从配置文件中取值
 * @return
 */
@ConfigurationProperties(prefix = "person")
@Bean
public Person person(){return new Person();
}

综上所述
@ConfigurationProperties 注解可能轻松的让配置文件跟实体类绑定在一起。

具备以下长处:

  • 注入属性反对批量,仅仅指定一个 前缀 prefix即可
  • 数据类型反对简单数据,比方List、Map
  • 属性名匹配规定 -涣散绑定 ,比方a-ba_baBA_B 都能够取值
  • 反对 JAVA 的 JSR303 数据校验

 值得关注的是:@ConfigurationProperties这个注解仅仅是反对从 Spring Boot 的默认配置文件 中取值,也就是 application.propertiesapplication.ymlapplication.yaml,那咱们如何从自定义配置文件取值呢???
 别着急,有解决办法,那就是再加一个注解:@PropertySource(value = "classpath:custom-profile.properties"), 上面会有对@PropertySource 注解的介绍。请急躁往下面看。

@Value
 @Value 这个注解咱们应该都比拟相熟了,Spring 中从属性取值的注解,反对 SPEL 表达式,不反对简单的数据类型,比方 Map、List。应用能够参考下面实体类外面的代码。

自定义配置文件并取值

Spring Boot在启动的时候会主动加载 application.xxx, 然而有的时候为了 防止 application.xxx 配置文件过于臃肿 ,就须要咱们 自定义配置文件 ,那么自定义配置文件的话,咱们如何从自定义配置文件外面取值呢?这时候就须要 配合 @PropertySource 这个注解 应用了。

应用 @PropertySource 注解

 在配置类上标注 @PropertySource 并指定你自定义的配置文件即可。能够参考上面代码

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.properties"}, encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {@Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}

对应配置文件

person.name=tinygrey
person.age=18
person.birthday=2020/11/23
person.hobby[0]= 打篮球
person.hobby[1]= 睡觉
person.hobby[2]= 玩游戏
person.hobby[3]= 学习
person.assets.phone=iphone 12
person.assets.car= 捷豹
person.dog.name= 奶狗
person.dog.age=3

@PropertySource 注解属性

value: 是一个 数组,能够指定多个配置文件 同时引入。
  value 是数组那么问题就来了:如果 同时加载多个配置文件 ,并且 不同配置文件 中对 同一个属性 设置了 不同的值,那么 Spring 会辨认哪一个呢?

创立两个配置文件custom-profile.yml、custom-profile1.yml,如上来引入。

@PropertySource(value = {"classpath:config/custom-profile.yml","classpath:config/custom-profile1.yml"})
public class Person {...}

咱们能够通过控制变量法进行测试,具体过程我这里就不赘述了。
间接说 论断 吧:Spring 加载程序 从左到右程序加载 ,后加载的会 笼罩 先加载的属性值。

另外须要留神的是 @PropertySource 默认加载 xxx.properties 类型 的配置文件,不能加载 YML 格局 的配置文件。如何解决呢?上面来解决这一问题

加载自定义 YML 格局的配置文件

@PropertySource注解有一个 属性 factory,默认值是 PropertySourceFactory.class,这个就是用来 加载 properties 格局 的配置文件,那咱们自定义一个用来 加载 YML 格局 的配置文件不就能够了嘛?上代码

/**
 * 解决 @PropertySource 只对 properties 文件能够进行加载,但对于 yml 或者 yaml 不能反对。*/
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {String sourceName = name != null ? name : resource.getResource().getFilename();
        if (!resource.getResource().exists()) {
            assert sourceName != null;
            return new PropertiesPropertySource(sourceName, new Properties());
        } else if (Objects.requireNonNull(sourceName).endsWith(".yml") || sourceName.endsWith(".yaml")) {Properties propertiesFromYaml = loadYamlProperties(resource);
            return new PropertiesPropertySource(sourceName, propertiesFromYaml);
        } else {return super.createPropertySource(name, resource);
        }
    }

    private Properties loadYamlProperties(EncodedResource resource){YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(resource.getResource());
        factory.afterPropertiesSet();
        return factory.getObject();}
}

 咱们写好下面的代码之后,只须要增加 @PropertySource 注解中 factory 属性指定为 YmlPropertySourceFactory 即可, 代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.yml"}, encoding = "utf-8", factory = YmlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "person")
public class Person {@Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}

对应配置文件:

person:
  name: qlh
  age: 22
  birthday: 2012/04/02
  hobby:
    - 打
    - 睡
    - 玩
    - 学
  assets:
    phone: iphone
    car: 福特野马
  dog:
    name: 狗
    age: 1

@PropertySource指定加载自定义的配置文件,默认只能加载 properties 格局 ,然而能够 指定 factory 属性来加载 YML 格局的配置文件
同时加载多个配置

测试是否胜利

编写 PropertiesController

@RestController
@RequestMapping("properties")
@Slf4j
public class PropertiesController {
    final
    Person person;

    public PropertiesController(Person person) {this.person = person;}
    @GetMapping("getProperties")
    public Dict getProperties(){log.info(person.toString());
        return Dict.create().set("person", person);
    }
}

浏览器输出:http://localhost:8081/springboot-properties/properties/getProperties验证后果。看到打印类信息,示意 加载自定义 YML 格局的配置文件 胜利了。

扩大性能

SpringBoot还提供了 @ImportResource 注解加载内部配置文件,只不过 @ImportResource 通常用于 加载 Spring 的 xml 配置文件

@ImportResource 应用

Spring Boot提出 零 xml 的配置 ,因而Spring Boot 默认状况 下是 不会被动辨认 我的项目中 Spring 的 xml 配置文件。为了可能 加载 xml 的配置文件 ,Spring Boot 提供了 @ImportResource 注解 ,该注解能够 加载 Spring 的 xml 配置文件 ,通常加于 启动类 上。这里就不做赘述了, 代码参考上面。

//value:Spring 的 xml 配置文件, 反对多个。@ImportResource(value = {"classpath:config/beans.xml"})
@SpringBootApplication
public class SpringbootPropertiesApplication {public static void main(String[] args) {SpringApplication.run(SpringbootPropertiesApplication.class, args);
    }

}

Spring Boot 多环境配置

多环境配置有什么益处呢???
 不同环境配置能够配置不同的参数
 便于部署,提高效率,缩小出错

yml 多环境配置

application.yml 主配置文件

server:
  port: 8081
  servlet:
    context-path: /springboot-properties
#配置激活选项
spring:
  profiles:
    active: dev

application-dev.yml 开发配置文件

# 指定属于哪个环境
spring:
  profiles:
    - dev

application-prod.yml 生产配置文件

# 指定属于哪个环境
spring:
  profiles:
    - prop

application-test.yml 测试配置文件

# 指定属于哪个环境
spring:
  profiles:
    - test

properties 多环境配置

(1)主配置文件:配置激活选项

spring.profiles.active=dev

(2)其余配置文件:指定属于哪个环境(同 yml,只不过表现形式是 key=value 的, 三个配置文件别离是:application-dev.properties,application-prod.properties,application-test.properties

yml 多环境配置和 properties 多环境配置比拟

Properties 配置多环境 : 须要增加多个配置文件
yml 配置多环境: 可增加多个配置文件,可不增加,应用--- 分隔(案例如上面代码)(不倡议应用该办法,这样显得配置文件臃肿,强烈建议增加多个配置文件,也不麻烦。)

server:
  port: 8081
  servlet:
    context-path: /springboot-properties
spring:
  profiles:
    active: dev
---
spring:
  profiles:
    - test
---
spring:
  profiles:
    - prod
---
spring:
  profiles:
    - dev

个别应用的配置文件
application.yml: 是主配置文件,放一些我的项目通用的配置
application-dev.yml: 放平时开发的一些配置,比如说数据库的连贯地址、帐号密码等
application-prod.yml: 放生产环境的一些配置,比如说数据库的连贯地址、帐号密码等
application-test.yml: 放测试环境须要用到的参数

激活指定 profile

  • 应用 spring.profiles.active 激活

 无论是应用上述 多文档块 的形式,还是新建 application-test.yml 文件,都能够在配置文件中指定 spring.profiles.active=test 激活指定的 profile。

  • 打成 jar 包运行时候应用命令行激活
java -jar XXXX-0.0.1-SNAPSHOT.jar --spring.profiles.active=test
  • 应用虚拟机参数激活
-Dspring.profiles.active=test

  • 在 java 代码中激活
@SpringBootApplication
public class SpringbootPropertiesApplication {public static void main(String[] args) {System.setProperty("spring.profiles.active", "test");
        SpringApplication.run(SpringbootPropertiesApplication.class, args);
    }

}

结束语

感激浏览小生文章。祝大家早日富可敌国,实现财产自在。
写文不易 , 肯定要 点赞、评论、珍藏哦 , 感激感激感激!!!

有任何问题能够在微信搜寻 公众号 Madison 龙少 进行征询
或者微信扫描上面二维码进行征询

退出移动版