[02] SpringBoot的项目属性配置

时间:2021-05-13 08:47:12

1、application.properties 简述

配置文件的使用和调整都非常方便,直接在项目默认的classpath下的application.properties文件中做调整即可。例如SpringBoot中默认的Tomcat毒启动端口是8080,我们需要调整为8081,那么直接在配置文件中调整server.port属性即可:
[02] SpringBoot的项目属性配置

默认的application配置文件是 .properties 格式,实际上你还可以使用 YAML 格式,即 .yml,如上图中的配置用 YAML 格式写则是:
[02] SpringBoot的项目属性配置

YAML 格式的配置文件,以缩进来表示属性之间的层级关系,从上面两种的对比我们已经能看出,YAML 格式的配置文件结构更加简洁清晰。当然,如果使用 YAML 格式的配置文件的话,还需要额外学习一下它的语法(阮一峰:YAML 语言教程),主要是结构表示,特别在书写时还应注意 缩进/空格 的使用,保证语法的正确性。

2、配置文件的加载顺序

(官方说明: 24.3 Application Property Files
SpringBoot应用会从如下四个位置加载application.properties文件,读取并载入到应用环境中去:
  • 当前目录下的 /config 目录
  • 当前目录
  • classpath下的 /config 目录
  • classpath 根目录

注意:如上的顺序是按照优先级排列的(从高到低),这意味着哪怕是同一个key的配置分别在不同位置的文件中,会优先选择 "当前目录下的 /config 目录"下的配置文件的值,也就是说,如果在优先级更高的位置找到了配置,则会无视优先级低的配置。这同样也意味着如果你想在测试/生产环境中快速修改配置参数,只需要在jar包之外的配置文件进行修改即可,不需要重新打包和部署应用。

这里的当前目录,是指与SpringBoot项目jar包同级的目录,如某个项目打包后的jar包在文件夹 /demo/springboot-demo.jar,则对应如上为:
  • /demo/config/application.properties
  • /demo/application.properties
  • /demo/springboot-demo.jar/BOOT-INF/classes/config/application.properties
  • /demo/springboot-demo.jar/BOOT-INF/classes/application.properties

当然,实际上展开来说SpringBoot读取配置的优先级不止上面的这几种,比如说优先级最高的实际上是命令行参数,即你在启动 jar 时通过命令行传入的配置参数。

3、自定义属性与加载

在类中,我们可以通过注解 @Value("${属性名}") 的方式将配置文件中的值注入到类中,如下配置文件有值:
server:
port: 8081 person:
name: zhangsan
age: 18
6
 
1
server:
2
  port: 8081
3

4
person:
5
  name: zhangsan
6
  age: 18

接着我们可以使用注解 @Value("${person.xxx}") 来将配置文件的值读入:
@Component
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
31
 
1
@Component
2
public class Person {
3
    @Value("${person.name}")
4
    private String name;
5
    @Value("${person.age}")
6
    private int age;
7

8
    public String getName() {
9
        return name;
10
    }
11

12
    public void setName(String name) {
13
        this.name = name;
14
    }
15

16
    public int getAge() {
17
        return age;
18
    }
19

20
    public void setAge(int age) {
21
        this.age = age;
22
    }
23

24
    @Override
25
    public String toString() {
26
        return "Person{" +
27
                "name='" + name + '\'' +
28
                ", age=" + age +
29
                '}';
30
    }
31
}

写个Controller看看吧:
[02] SpringBoot的项目属性配置

4、@ConfigurationProperties

自定义属性与加载中提到的 @Value 方法,在配置较多时仍然显得有些繁重,SpringBoot提供了更简洁的方式,即 @ConfigurationProperties,该注解还有一个属性 prefix,可以指定配置文件的前缀,并且对类按属性名进行自动匹配。如上例中的 Person,换用 @ConfigurationProperties 的话,则如下:
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
30
 
1
@Component
2
@ConfigurationProperties(prefix = "person")
3
public class Person {
4
    private String name;
5
    private int age;
6

7
    public String getName() {
8
        return name;
9
    }
10

11
    public void setName(String name) {
12
        this.name = name;
13
    }
14

15
    public int getAge() {
16
        return age;
17
    }
18

19
    public void setAge(int age) {
20
        this.age = age;
21
    }
22

23
    @Override
24
    public String toString() {
25
        return "Person{" +
26
                "name='" + name + '\'' +
27
                ", age=" + age +
28
                '}';
29
    }
30
}

注意添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
5
 
1
<dependency>
2
    <groupId>org.springframework.boot</groupId>
3
    <artifactId>spring-boot-configuration-processor</artifactId>
4
    <optional>true</optional>
5
</dependency>

5、参数间的引用

在 application.properties 文件中,各个参数也可以相互引用,仍然是通过 ${属性名} 的方式,如下 zhangsan.name 就会引用 person.name 的值,age同理:
server:
port: 8081 person:
name: zhangsan
age: 18 zhangsan:
name: ${person.name}
age: ${person.age}
10
 
1
server:
2
  port: 8081
3

4
person:
5
  name: zhangsan
6
  age: 18
7
  
8
zhangsan:
9
  name: ${person.name}
10
  age: ${person.age}

6、使用随机值

在某些情况下,我们希望某些配置不是一个固定的值,那么可以在配置文件中使用 ${random.xx} 来产生int、long、String等随机值。如:
# 随机字符串
person.value=${random.value}
person.name=${random.string} # uuid
person.name=${random.uuid} # 随机int
person.age=${random.int} # 随机long
person.height=${random.long} # 10以内的随机数
preson.age=${random.int(100)} # 10-20的随机数
person.name=${random.int[10,20]}
18
 
1
# 随机字符串
2
person.value=${random.value}
3
person.name=${random.string}
4

5
# uuid
6
person.name=${random.uuid}
7

8
# 随机int
9
person.age=${random.int}
10

11
# 随机long
12
person.height=${random.long}
13

14
# 10以内的随机数
15
preson.age=${random.int(100)}
16

17
# 10-20的随机数
18
person.name=${random.int[10,20]}

7、Environment

这种方式是通过依赖注入Spring的Environment类实现的,并通过该类的getProperty方法配置的值:
@RestController
@RequestMapping("/demo/")
public class DemoController { @Autowired
private Environment environment; @RequestMapping("/printPerson.do")
public String printPerson() {
String personName = environment.getProperty("person.name");
return personName;
} }
14
 
1
@RestController
2
@RequestMapping("/demo/")
3
public class DemoController {
4

5
    @Autowired
6
    private Environment environment;
7

8
    @RequestMapping("/printPerson.do")
9
    public String printPerson() {
10
        String personName = environment.getProperty("person.name");
11
        return personName;
12
    }
13

14
}

另外在测试时发现一个小坑值得注意,当配置文件中的 person.name 值采用了 ${random.value},并绑定到 Person 类中,你会发现 environment 再每次获取 person.name 的值时是随机的,但是Person类中通过person.name赋予的值只是第一次的值,之后将不会主动改变:
@RestController
@RequestMapping("/demo/")
public class DemoController { @Autowired
private Person person; @Autowired
private Environment environment; @RequestMapping("/printPerson.do")
public String printPerson() {
String nameFromClass = person.getName();
String nameFromEnv = environment.getProperty("person.name");
System.out.println("class: " + nameFromClass);
System.out.println("env: " + nameFromEnv);
return nameFromEnv;
} }
20
 
1
@RestController
2
@RequestMapping("/demo/")
3
public class DemoController {
4

5
    @Autowired
6
    private Person person;
7

8
    @Autowired
9
    private Environment environment;
10

11
    @RequestMapping("/printPerson.do")
12
    public String printPerson() {
13
        String nameFromClass = person.getName();
14
        String nameFromEnv = environment.getProperty("person.name");
15
        System.out.println("class: " + nameFromClass);
16
        System.out.println("env: " + nameFromEnv);
17
        return nameFromEnv;
18
    }
19

20
}

分别访问三次,得到输出如下:
class: 80c628c7948c4270f831956945a0f26e
env: d8ff3f804268a929d365c663ade6894c
class: 80c628c7948c4270f831956945a0f26e
env: 9351315473098298f8416dae16bd1acb
class: 80c628c7948c4270f831956945a0f26e
env: ef78a4148b049c3fc25a71bddb560715
6
 
1
class: 80c628c7948c4270f831956945a0f26e
2
env: d8ff3f804268a929d365c663ade6894c
3
class: 80c628c7948c4270f831956945a0f26e
4
env: 9351315473098298f8416dae16bd1acb
5
class: 80c628c7948c4270f831956945a0f26e
6
env: ef78a4148b049c3fc25a71bddb560715

[02] SpringBoot的项目属性配置

8、多环境配置 application-{profile}

我们在实际开发时,开发环境配置、测试环境配置、正式生产环境配置,配置信息都是不一样的(如开发环境的Tomcat端口是8080,生产环境的Tomcat端口则是8089),在频繁修改配置的过程中很容易出错。SpringBoot提供了更加简单的方式来解决这种问题,即通过配置多份不同环境下的配置文件。如原来仅有 application.yml,现在则新增三项,分别命名为:
  • application-dev.yml
  • application-test.yml
  • application-prod.yml

而在主配置文件 application.yml 中,则通过属性 spring.profiles.active 来指定使用哪份配置文件,如在开发时则指定 spring.profiles.active=dev,生产环境时 spring.profiles.active=prod
[02] SpringBoot的项目属性配置

也可以在命令行启动时进行配置,这样哪怕重新部署也不需要修改application.yml:
java -jar xxx.jar --spring.profiles.active=prod
1
 
1
java -jar xxx.jar --spring.profiles.active=prod

9、加载多个自定义配置文件

我们当然喜欢多项配置能独立在不同的配置文件中进行管理,隔离而清晰,这就要求SpringBoot能够加载多个配置文件。如我们在 src/main/resource 下新建目录 config,新建配置文件 person.properties:
[02] SpringBoot的项目属性配置

那么我们需要在Person类的注解上新增两个注解:@Configuration 和 @PropertySource,其中用 @PropertySource 注解中的 value 属性来指定需要读取的配置文件的路径,如下:
@Component
@Configuration
@PropertySource("classpath:config/person.properties")
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
32
 
1
@Component
2
@Configuration
3
@PropertySource("classpath:config/person.properties")
4
@ConfigurationProperties(prefix = "person")
5
public class Person {
6
    private String name;
7
    private int age;
8

9
    public String getName() {
10
        return name;
11
    }
12

13
    public void setName(String name) {
14
        this.name = name;
15
    }
16

17
    public int getAge() {
18
        return age;
19
    }
20

21
    public void setAge(int age) {
22
        this.age = age;
23
    }
24

25
    @Override
26
    public String toString() {
27
        return "Person{" +
28
                "name='" + name + '\'' +
29
                ", age=" + age +
30
                '}';
31
    }
32
}

这里不再使用 person.yml 作为配置文件,是因为 @PropertySource 注解不支持YAML文件格式,官方说明 “YAML files cannot be loaded by using the @PropertySource annotation. So, in the case that you need to load values that way, you need to use a properties file.”  看来在SpringBoot中,作为配置文件,还是使用 .properties 更为兼容。

10、打包jar时需要注意的自定义配置文件打包

在IDE上运行时正常,在打包项目为jar时再运行就提醒找不到配置文件,而这些配置文件往往是我们自定义的。我们到打包后的jar中目录下 BOOT-INF/classes 中发现,自定义的配置文件没有打包进来,这时需要注意,在pom.xml中添加 <resource>:
<build>
<!-- 打包资源文件 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</build>
11
 
1
<build>
2
    <!-- 打包资源文件 -->
3
    <resources>
4
        <resource>
5
            <directory>src/main/resources</directory>
6
            <includes>
7
                <include>**/*</include>
8
            </includes>
9
        </resource>
10
    </resources>
11
</build>

其中 <include> 中的 **/* 表示通配符,所有目录下的所有文件,你也可以 **/*.properties 诸如此类。

11、@Configuration 和 @Bean

这个具体来说更应该是bean的注解配置方式:
  • @Configuration 标注在类上,相当于spring的xml配置文件中的 <beans>,作用为配置spring容器(应用上下文)

那么相应的
  • @Bean 标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的 <bean>,作用为注册bean对象


12、参考链接