SpringBoot学习笔记(二):SpringBoot的配置文件(1)

一、SpringBoot配置文件的介绍

SpringBoot中的配置文件在resources目录下,它可以修改SpringBoot自动配置的默认值。配置文件的名称是固定的(application),但配置文件格式有两种,分别为:properties文件和yaml/yml文件。这里以修改启动端口为例,演示一下两种格式的配置文件中的配置:

application.properties:

server.port=8081

application.yaml:

server:
  port: 8080

二、yaml文件的介绍

yaml文件基本语法如下:

k:(空格)v:表示一对键值对(空格必须有)

注意:

  • yaml文件的基本语法是以空格的缩进来控制层级关系。
  • 只要是左对齐的一列数据,都是同一个层级的,这方式和Python的语法很相似。
  • 属性和值也是大小写敏感。

1、yaml中值的写法

(1)字面量(普通的值),如:数字、字符串、布尔值。

语法:k: v

几点说明:

a. 字符串默认不用加上单引号或者双引号;

b. "":双引号不会转义字符串里面的特殊字符,而特殊字符会作为本身想表示的意思。例如:name: "zhangsan \n lisi"。输出:zhangsan 换行 lisi

c. '':单引号会转义特殊字符,特殊字符最终只是一个普通的字符串数据。例如:name: ‘zhangsan \n lisi’。输出:zhangsan \n lisi

(2)对象、Map(属性和值)

语法:k: v

在下一行来写对象的属性和值的关系

a.普通写法

person:
  name: 刘婷
  age: 23

b.行内写法 

teacher: {name: 刘玉,age: 31}

(3)数组(List Set)

语法:用- 值表示数组中的一个元素。

a.普通写法

bookList:
  - 西游记
  - 水浒传
  - 三国演义
  - 红楼梦

b.行内写法

personSet: [刘备,曹操,孙权]

2、SpringBoot配置文件值注入之@ConfigurationProperties注解

在前面的内容不难发现我们可以在SpringBoot的配置文件中定义不同的数据类型,而这些数据也是可以在程序中使用的。下面就介绍一下SpringBoot配置文件值注入。

(1)编写application.yaml

teacher:
  name: tom
  age: 40
  birth: 1981/09/21
  email: [email protected]
  isProfessor: false
  bookList:
    - 三国演义
    - 水浒传
    - 红楼梦
    - 西游记
  scoreMap: {语文: 95, 数学: 87, 英语: 82}
  student:
    name: 刘菲
    address: 浙江

(2)创建实体类Teacher和Student。

Teacher:

package com.yht.entity;

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

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @ConfigurationProperties: 本类中的所有属性都和配置文件中的值进行绑定
 */
@Component
@ConfigurationProperties(prefix = "teacher")
public class Teacher {
    private String name;
    private Integer age;
    private Date birth;
    private String email;
    private Boolean isProfessor;
    private List<String> bookList;
    private Map<Object, Object> scoreMap;
    private Student student;


    public Teacher() {
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Boolean getProfessor() {
        return isProfessor;
    }

    public void setProfessor(Boolean professor) {
        isProfessor = professor;
    }

    public List<String> getBookList() {
        return bookList;
    }

    public void setBookList(List<String> bookList) {
        this.bookList = bookList;
    }

    public Map<Object, Object> getScoreMap() {
        return scoreMap;
    }

    public void setScoreMap(Map<Object, Object> scoreMap) {
        this.scoreMap = scoreMap;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birth=" + birth +
                ", email='" + email + '\'' +
                ", isProfessor=" + isProfessor +
                ", bookList=" + bookList +
                ", scoreMap=" + scoreMap +
                ", student=" + student +
                '}';
    }
}

Student:

package com.yht.entity;

/**
 * @author yht
 * @description: TODO
 * @date 2021/2/258:13
 */
public class Student {
    private String name;
    private String address;

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

(3)编写测试类。

@SpringBootTest(classes = HelloApplication.class)
@RunWith(SpringRunner.class)
public class PropertiesTest {
    @Autowired
    Teacher teacher;
    @Test
    public void testPerson(){
        System.out.println(teacher);
    }
}

(4)错误总结

错误一:

错误信息:Spring Boot Configuration Annotation Processor not found in classpath

原因:缺少 Spring Boot 引导配置依赖

解决方案:在pom.xml中加入依赖如下:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

错误二:

错误信息:Failed to load property source from location 'classpath:/application-dev.yml

原因:将application.yml添加到classpath时,由于变更了application.yml的编码格式(或许也改变了些代码内容)。, 而target内的yml文件没有实时更新, 从而导致了 Failed to load property source from location 'classpath:/application.yml' 的错误。如下可以看出yaml中的配置文件出现乱码。

解决办法: 可以直接修改target内的yml 保存为原yml 一样的编码和内容即可。

错误三:

错误信息:Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test

原因:没在根目录下写一个Spring Boot的启动类。

解决方案:这种错误的解决方案有多种,可参考此博客:https://blog.csdn.net/csdn_am/article/details/79757097。我采取的解决方案是指定SpringBoot的启动类,即在注解上加上@SpringBootTest(classes = HelloApplication.class)。

注意:

如果将yaml文件用properties文件代替,通过上述的测试代码同样可以得到Teacher的数据。

teacher.name=李四
teacher.age=30
teacher.birth=1991/04/12
[email protected]
teacher.professor=false
teacher.book-list=1,2,3,4,5
teacher.score-map.a=Apple
teacher.score-map.b=Bar
teacher.score-map.c=car
teacher.student.name=小王
teacher.student.address=福建

3、SpringBoot配置文件值注入之@Value注解

与上述方式不同在于:这种方式只需要在实体类的属性前面加上@Value即可。示例如下:

@Component
public class Teacher {
    @Value("${teacher.name}")
    private String name;
    @Value("#{12*4}")
    private Integer age;
    @Value("${teacher.birth}")
    private Date birth;
    @Value("${teacher.email}")
    private String email;
    @Value("${teacher.professor}")
    private Boolean isProfessor;
    private List<String> bookList;
    private Map<Object, Object> scoreMap;
    private Student student;
    //省略get和set方法
}

4、@ConfigurationProperties和@Value方式的对比

  @ConfigurationProperties @Value
功能 批量注入配置文件中的属性 每个属性单独指定
松散绑定(松散语法) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持

应用场景

  • 如果在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value。
  • 如果专门编写了一个javaBean来和配置文件进行映射,就直接使用@ConfifigurationProperties。

说明:

(1)松散绑定

所谓的松散绑定是指yaml文件中写的first-name,这个和实体类代码中的firstName是一样的, - 后面跟着的字母默认是大写的,也就是-和我们的小写驼峰匹配。三种写法:

a.方式一:标准方式

person.firstName

b.方式二:大写用-
person.first-name

c.方式三:大写用_
person.first_name

(2)SpEL

Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等,并且能与Spring功能完美整合,如:能用来配置Bean定义。

SpEL支持如下表达式:

  • 基本表达式: 字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式;
  • 类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用;
  • 集合相关表达式: 内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义;
  • 其他表达式:模板表达式。

注:SpEL表达式中的关键字是不区分大小写的。在上面@Value的实例中,age属性值采用了SpEL。SpEL在这里不再赘述。

(3)数据校验

数据校验需要用到两个注解@Validated和 @Email,这里以校验邮箱地址为例:

@Component
@ConfigurationProperties(prefix = "teacher")
@Validated
public class Teacher {
    @Email
    private String email;
}

5、SpringBoot中boolean值的命名问题

在前面的实体类中有一个问题,那就是布尔类型的isProfessor属性,我的命名是isProfessor,当通过yaml文件指定值的属性时,得到的结果中isProfessor值为null,如下:

在properties文件同样如此。具体原因还没找到,不过大家切记不要给boolean类型的属性取名带"is"前缀。或者说在yaml或者properties文件中配置isProfessor值时可以写为(以properties文件为例):professor=false,就不要写is前缀了。如果有知道原因的小伙伴,还请评论。

猜你喜欢

转载自blog.csdn.net/weixin_47382783/article/details/114045263