SpringBoot配置方式和相关注解

1,981 阅读6分钟

配置方式

配置文件

SpringBoot使用一个全局的配置文件: application.properties或application.yml(或yaml),其中文件名是固定的。

YAML文件配置

以往的配置文件大多都是xml文件,而YAML则是以数据为中心,比json,xml等更适合做配置文件。

语法

以空格的缩进来控制层级关系(空格多少皆可),只要是左对齐的一列数据,都是同一个层级的。 数据的格式是k: v,需要注意冒号 和值中间是有一个空格的,并且属性和值区分大小写。

server:
    port: 8081
    path: /hello

值的写法

(1)值可以是数字,字符串,布尔,且字符串默认不用加单/双引号。 如果加上单/双引号,例如:

 name: "zhangsan \n lisi"

在双引号里 特殊字符会作为本身想表示的意思,因此会输出zhangsan,然后换行,再输出 lisi

 name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi

在单引号里特殊字符只是一个普通的字符串数据,因此输出 zhangsan \n lisi

(2)值还可以作为对象,例如:

friends:
   lastName: zhangsan
   age: 20

此外还可以写成一行:

friends: {lastName: zhangsan,age: 20}

(3)值也可以作为数组里的一个元素

pets:
 - cat
 - dog
 - pig

同样也可写成一行:

pets: [cat,dog,pig]

配置文件值注入

导入依赖:

<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

配置文件application.yml:

person:
  lastName: on1
  age: 20
  isStu: false
  birth: 2019/02/15
  maps: {k1: v1, k2: 12}
  list:
    - C2y
    - sngu
  cat:
    name: 小猫
    age: 2

bean类:

/*
@ConfigurationProperties注解将本类中所有属性与配置文件中相关的配置进行绑定
    prefix属性表示与配置文件中哪个属性对应
加上@Component表示该组件是容器里的组件,这样才能使用容器提供的ConfigurationProperties
 */

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean isStu;
    private Date birth;

    private Map<String, Object> maps;
    private List<Object> list;
    private Cat cat;
    //省略get / set,toString方法    
}
public class Cat {
    private String name;
    private Integer age;    
    //省略get / set,toString方法    
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
class HaloworldQuickstartApplicationTests {
    @Autowired
    Person person;

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

properties文件配置

person.last-name=张三
person.age=18
person.birth=2017/02/15
person.is-stu=false
person.maps.k1=v1
person.maps.k2=15
person.list=a,b,c
person.cat.name=cat
person.cat.age=3

通过下图将配置文件字符集改为UTF-8来解决中文乱码:

控制台输出:

@Value注解

前面的例子我们使用的是@ConfigurationProperties注解将本类中所有属性与配置文件中相关的配置进行绑定,除此之外还可以使用@value注解来一个个的指定属性值。

@Component
public class Person {
    @Value("${person.last-name}")
    private String lastName;
    @Value("#{3 * 2}")
    private Integer age;
    @Value("true")
    private Boolean isStu;
//省略其他属性和get/set,toString方法
}

@ConfigurationProperties和@Value的对比

(1)@ConfigurationProperties是批量注入配置文件中的属性,而@Value是一个个注入

(2)@ConfigurationProperties支持松散绑定,而@Value不支持

在说明松散绑定前先介绍一下属性名匹配规则:

  • person.first-name:该写法表示-后面的字母是大写
  • person.first_name:该写法表示_后面的字母是大写
  • person.firstname:标准写法
  • PERSON_FIRST_NAME:推荐系统属性使用该写法

如果上例中的注入属性lastName的写法改为:

@Value("${person.lastName}")

则会报错,而如果是@ConfigurationProperties配置的类,则将配置文件的属性赋值改为:

person.lastName=张三

也是可以的。

(3)@ConfigurationProperties不支持SpEl,而@Value支持

上例中@Value注解可以是这种写法:

@Value("#{3 * 2}")

但如果是@ConfigurationProperties注解,则不允许,如果在配置文件中

(4)@ConfigurationProperties支持JSR303数据校验,而@Value不支持

比如此处我们使用@Max注解来检验配置文件中的age是否是大于33的,若是则会报错。

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
    private String lastName;
    @Max(33)
    private Integer age;
    //...
}

但如果是@Value来注入,则不会报错。

@Component
//@ConfigurationProperties(prefix = "person")
@Validated
public class Person {

    @Value("${person.lastName}")
    private String lastName;
    
    @Value("${person.age}")
    @Max(33)
    private Integer age;
    //...
}

(5)@ConfigurationProperties支持复杂类型封装,而@Value不支持

比如上例中使用

@Value("${person.maps}")
private Map<String, Object> maps;

则会报错,@Value只支持一些基本类型,list、数组的封装。

总结来说,如果我们只是在某个业务逻辑中获取一下配置文件中的某项值,则使用@Value,如下例所示。

@RestController
public class HaloController {
    @Value("${person.lastName}")
    private String name;

    @RequestMapping("/halo")
    public String hello() {
        return "hello " + name;
    }
}

如果是有一个专门的javaBean类和配置文件映射,则使用@ConfigurationProperties

@PropertySource注解

除了将属性值放在application.properties和application.yml外,我们还可以在resources文件夹下单独写一个person.properties作为映射文件。此时我们就需要@PropertySource注解来指定文件名。

示例

@Component
@PropertySource(value = {"classpath:person.properties"})
@ConfigurationProperties(prefix = "person")
public class Person {
    //...
}
person.lastName=呱呱
person.age=35
person.birth=2017/02/15
person.is-stu=false
person.maps.k1=v1
person.maps.k2=15
person.list=a,b,c
person.cat.name=cat
person.cat.age=3

然后将application.properties里的person类映射注释掉,即可成功映射。

该注解仅支持properties文件,不支持yaml文件

相关注解

@ImportResource注解

SpringBoot里没有Spring配置文件,它不会自动识别我们自己编写的配置文件,因此我们需要通过注解来加载我们写的Spring配置文件。

示例

比如我们在resources下写一个Spring配置文件并定义一个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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="HaloService" class="com.on1.haloworldquickstart.HaloService">
    </bean>
</beans>

在主配置类下通过注解@ImportResource声明Spring文件名

@ImportResource(locations = {"classpath:beans.xml"})
@SpringBootApplication
public class HaloworldQuickstartApplication {

    public static void main(String[] args) {
        SpringApplication.run(HaloworldQuickstartApplication.class, args);
    }

}

测试一下,输出为true。

@RunWith(SpringRunner.class)
@SpringBootTest
public class HaloworldQuickstartApplicationTests {
    @Autowired
    Person person;

    @Autowired
    ApplicationContext ioc;

    @Test
    public void ifHasBean() {
        System.out.println(ioc.containsBean("helloService"));
    }
}

@Bean

上述通过注解的方式来加载Spring文件过于麻烦,SpringBoot推荐通过配置类代替配置文件的方式来向容器里注入组件,即使用全注解的方式。

示例

package com.on1.haloworldquickstart.service;

public class HelloService {
}

将上例的@ImportResource注解删掉,创建一个配置类(也可以在主配置类里操作):

@Configuration
public class MyAppConfig {
//    将返回类型对象添加到容器里,组件的id值默认是方法名
    @Bean
    public HelloService helloService() {
        System.out.println("正在向容器里添加组件");
        return new HelloService();
    }
}

测试一下,输出为true。

@RunWith(SpringRunner.class)
@SpringBootTest
class HaloworldQuickstartApplicationTests {
    @Autowired
    ApplicationContext ioc;

    @Test
    void ifHasBean() {
        System.out.println(ioc.containsBean("helloService"));
    }
}

配置文件的相关事项

配置文件占位符

我们可以在映射文件里 使用随机数:

person.lastName=张三 ${random.uuid}
person.age=${random.int}
person.birth=2017/02/15
person.is-stu=false
person.maps.k1=v1
person.maps.k2=15
person.list=a,b,c
person.cat.name=${person.lastName}_cat
person.cat.age=3
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    //属性值与上例一样
}

测试方法

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

输出:

此例person.cat.name获取了前面配置的属性值person.lastName,如果获取的属性值不存在,则我们可以指定默认值,如下例的person.halo。

person.cat.name=${person.halo:halo}_cat

输出:

Profile

Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境

properties文件的分支配置

文件名格式为:application-{分支名}.properties,比如此处我们命名为application-dev.properties,该文件内容为:

server.port=8082

程序默认加载主配置文件application.properties,因此我们需要在指定加载哪个配置文件:

spring.profiles.active=dev

运行后端口号变为8082:

yml文件的分支配置

注释掉前面的指定:spring.profiles.active=dev

在application.yml

#  通过spring.profiles.active指定是哪个分区的环境
spring:
  profiles:
    active: dev
---
server:
  port: 8083
spring:
  profiles: dev
---
server:
  port: 8085
spring:
  profiles: prod
---

运行后即可发现端口号为dev分区的端口号8083:

(2)此外还可以在Program arguments中指定分区名

运行后即可发现端口号为prod分区的端口号8085:

默认配置文件加载顺序

SpringBoot启动会按以下顺序来扫描application.properties/yml文件作为默认配置文件。如下图所示:优先级低的先配置,高优先级的配置会覆盖低优先级的配置,并且是互补配置。

  • 项目文件下的某一文件里的application.properties/yml配置文件
  • 项目文件下里的application.properties/yml配置文件
  • resources文件里某一文件下的application.properties/yml配置文件
  • resources文件里的application.properties/yml配置文件