SpringBoot学习篇|Yaml配置文件属性注入

2023-05-17 10:50:58 浏览数 (2)

SpringBoot学习篇|Yaml配置文件属性注入

/resources/application.properties可以配置哪些内容呢?

参考: 官方配置文档

application.properties–>application.yaml

首先可以看一下从哪里知道可以导入yaml的(实际上官方更推荐使用yaml)

我们可以从当前项目的pom.xmlorg.springframework.boot的.xml配置文件看一下include标签可见一下内容

语法:

application.properties:

​ key=value

application.yaml:

​ key:空格value

可以看到没有空格的时候配置是无效的

yaml的配置示例:

代码语言:javascript复制
#对空格的要求很严格.
#最强大在于可以注入到配置类中!!!!
#键值对
name: h0cksr
#对象
person1:
  name: h0cksr
  age: 18
  face:
    eyes: 2
    mouth: 1
#对象的行内写法
person2: {name: h0cksr,age: 18, face: {eyes: 2,mouth: 1}}
#数组
pets1:
  - cat
  - dog
  - pig
#数组写法二
pets2: [cat,dog,pig]

properties配置示例:

代码语言:javascript复制
#相比之下单调得多,只有一种语法
name=h0cksr

person.name=h0cksr
person.age=18
person.face.eyes=2
person.face.mouth=1

使用示例(yaml文件属性注入)

方法一:application.yaml

我们直接将application.properties删除后把上面的yaml示例写入/resources/application.yaml

Person类:

代码语言:javascript复制
package com.example.demo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component//注册Bean,只有注册Bean之后才能通过ConfigurationProperties注释导入yaml属性配置
@ConfigurationProperties(prefix = "person1")//将person1对象的属性读取并且赋值给同变量名的成员
public class Person {
    private String name;
    private Integer age;
    private Map<String,Object> face;

    public Person(){
    }
    public Person(String name,Integer age,Map<String,Object> face){
        this.name=name;
        this.age=age;
        this.face=face;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setFace(Map<String, Object> face) {
        this.face = face;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public Map<String, Object> getFace() {
        return face;
    }

    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                ", face="   face  
                '}';
    }
}

重写测试类com.example.demo.Demo3ApplicationTests

代码语言:javascript复制
package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.function.context.test.FunctionalSpringBootTest;

import java.io.IOException;

@SpringBootTest
public class Demo3ApplicationTests {
    @Autowired
    private Person person;

    @Test
    void contextLoads() throws IOException {
        System.out.println(person);
    }
}

运行com.example.demo.Demo3ApplicationTests

可以看到yaml的对象属性值已经赋值到了这个Person类对象属性中

理解:

@Component@ConfigurationProperties注解让Person类被注册为Bean并且将person1的对象属性赋值给当前全部变量

如果Person不是一个Bean的话也会报错

@Autowired注释会自动调用构造函数,并调用Person类的setter方法将yaml对象的属性赋值给新对象

注:如果缺少属性的setter方法就会导致程序报错,如果yaml文件对象中缺少Person对应的属性的话输出该属性为Null

方法二

可以通过@Value注解设置指定的默认值

代码语言:javascript复制
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
public class Person {
    @Value("hcksr")
    private String name;
    @Value("18")
    private Integer age;
    private Map<String,Object> face;
    public Person(){
    }
    public Person(String name,Integer age,Map<String,Object> face){
        this.name=name;
        this.age=age;
        this.face=face;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setFace(Map<String, Object> face) {
        this.face = face;
    }
    public String getName() {
        return name;
    }
    public Integer getAge() {
        return age;
    }
    public Map<String, Object> getFace() {
        return face;
    }
    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                ", face="   face  
                '}';
    }
}

方法三:xxx.yaml

默认情况下是会自动读取application.yamlapplication.properties配置文件的,但是如果配置文件名不是appincation的时候要怎样将文件配置属性注入到类中呢?

答案是通过使用一个新的注释@PropertySource导入指定配置文件的属性

这种方法可以导入一个文件的全部属性,但是并不会自动赋值,需要我们自己通过@Value注解使用SPEL表达式获取属性值,但貌似会有些限制(也可能是我表达式的问题,并不能直接获取一个对象或者一个Map例如face进行赋值)

以下为name和age参数的获取示例:

h0cksr.yaml
代码语言:javascript复制
name: {random.uuid}
age:{random.int}
face:
  eyes: 22
  mouth: 11
person:
  name: h0cksr
  age: 18
  face:
    eyes: 2
    mouth: 1
代码语言:javascript复制
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
//@PropertySource(value = "classpath:h0cksr.properties")//导入h0cksr.properties和h0cksr.yaml的内容
@PropertySource(value = "classpath:h0cksr.yaml")
public class Person {
    @Value("{name}")//默认获取到的是person.name: h0cksr, 使用{person.name}会报错
    private String name;
    @Value("{age}")//获取到的是person.age: 18,使用{person.age}会报错
    private Integer age;
//    @Value("{face}") //{face}获取对象赋值失败,和.properties一样不能通过${xxx}获取带有属性的对象
    private Map<String,Object> face;
    public Person(){
    }
    public Person(String name,Integer age,Map<String,Object> face){
        this.name=name;
        this.age=age;
        this.face=face;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setFace(Map<String, Object> face) {
        this.face = face;
    }
    public String getName() {
        return name;
    }
    public Integer getAge() {
        return age;
    }
    public Map<String, Object> getFace() {
        return face;
    }
    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                ", face="   face  
                '}';
    }
}
h0cksr.properties
代码语言:javascript复制
name={random.uuid}
age={random.int}
face.eyes=22
face.mouth=11
person.name=h0cksr
person.age=18
person.face.eyes=2
person.face.mouth=1
代码语言:javascript复制
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
@PropertySource(value = "classpath:h0cksr.properties")//导入h0cksr.properties和h0cksr.yaml的内容
//@PropertySource(value = "classpath:h0cksr.yaml")
public class Person {
    @Value("{name}")//默认为{random.uuid},使用{person.name}获得h0cksr
    private String name;
    @Value("{age}")//默认为{random.int},使用{person.age}获得18
    private Integer age;
//    @Value("{face}") //不能通过{}获取到一个有属性的对象,只能获取到具体的单一一个属性例如{face.eyes}或{person.face.eyes}
    private Map<String,Object> face;
    public Person(){
    }
    public Person(String name,Integer age,Map<String,Object> face){
        this.name=name;
        this.age=age;
        this.face=face;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setFace(Map<String, Object> face) {
        this.face = face;
    }
    public String getName() {
        return name;
    }
    public Integer getAge() {
        return age;
    }
    public Map<String, Object> getFace() {
        return face;
    }
    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                ", face="   face  
                '}';
    }
}

小结

不管是.yaml还是.properties都可以写SPEL表达式

另外也可以在@Value注释写SPEL表达式获取导入的变量属性

${}的SPEL取值类型是有限制的,对于更多类型的取值需要用到其它的SPEL语法,这点在这里就不过多赘述,感兴趣可以参考下面两个链接:

https://www.jianshu.com/p/e0b50053b5d3

http://c.biancheng.net/spring/spel.html

松散绑定

application.yaml

代码语言:javascript复制
pe-r-son:
  na-me: h0cksr
  a-g-e: 18
  fa-ce:
    eyes: 2
    mouth: 1
代码语言:javascript复制
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Map<String,Object> face;
    public Person(){
    }
    public Person(String name,Integer age,Map<String,Object> face){
        this.name=name;
        this.age=age;
        this.face=face;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setFace(Map<String, Object> face) {
        this.face = face;
    }
    public String getName() {
        return name;
    }
    public Integer getAge() {
        return age;
    }
    public Map<String, Object> getFace() {
        return face;
    }
    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                ", face="   face  
                '}';
    }
}

这里的Person其实就是上面方法一中的代码,但是运行后可以看到此时application.yaml中的属性名为pe-r-son,na-me,a-g-e,fa-ce但是这几个属性却被作为prefix的person参数读取到并且准确无误地注入到了Person的name,age,face当中,这就是松散绑定的效果了

相比之下,如果使用@Value获取属性的话那松散绑定就会失效了

JSR303校验

如果对Person类加了@Validated注释,那么就可以对类的属性进行类型校验,可以通过给属性添加注解检测属性的格式是否为长度,日期,邮箱等格式或其它格式,如果不是则报错,具体使用方法可以参考下面链接

https://www.jianshu.com/p/554533f88370

总结

  • 配置yml和配置properties都可以获取到值,强烈推荐yaml
  • 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下@value
  • 如果说,我们专门编写了一个JavaBean来和配置文件进行映射,就直接使用configurationProperties,不要犹豫!

0 人点赞