Spring Boot 学习之路 -- 配置项目
前言
- 最近因为业务需要,被拉去研究后端的项目,代码基于 Spring Boot,对我来说完全小白,需要重新学习研究…
- 出于个人习惯,会以 Blog 文章的方式做一些记录,文章内容基本来源于「 Spring Boot 从入门到精通(明日科技) 」一书,做了一些整理,更易于个人理解和回顾查找,所以大家如果希望更系统性的学习,可以阅读此书(比较适合我这种新手)。
一、Spring Boot 项目的配置文件
程序开发人员在开发 Spring Boot 项目的过程中,会发现 Spring Boot 非常好用。这是因为 Spring Boot 项目所需要的数据都在其配置文件中完成了配置。配置文件中的数据有很重要的作用。例如,如果在一个 Spring Boot 项目的配置文件中缺少关于数据库的数据,这个 Spring Boot 项目就无法连接、操作数据库。在一个 Spring Boot 项目的配置文件中,有些数据在创建这个 Spring Boot 项目时就完成了配置,有些数据则需要程序开发人员进行配置。
下面就介绍程序开发人员如何对 Spring Boot 项目的配置文件中的数据进行配置。
当创建一个 Spring Boot 项目时,就会在其中的 src/main/resources 目录下自动创建一个 application.properties 文件,该文件就是这个 Spring Boot 项目的配置文件。
程序开发人员在配置 Spring Boot 项目的过程中,会在配置文件中配置该项目所需的数据信息。这些数据信息被称作“配置信息”。那么,“配置信息”都包含哪些内容呢?“配置信息”的内容非常丰富,这里仅举例予以说明:
- Tomcat服务器
- 数据库的连接信息,即用于连接数据库的用户名和密码
- Spring Boot 项目的启动端口
- 第三方系统或者接口的调用密钥信息
- 用于发现和定位问题的日志
我们回顾一下【Spring Boot 学习之路 – 基础认知】 中第一个 Spring Boot 程序,会发现并没有对这个程序做任何配置,仅编写了几行代码,这个程序就能够实现一个简单的跳转功能。其实,这就凸显了 Spring Boot 的强大之处:当创建一个 Spring Boot 项目时,有些配置信息就已经在配置文件中被配置完成了,不需要程序开发人员予以配置,这提高了开发程序的效率。
下面将先对配置文件的格式予以介绍。
1.1 配置文件的格式
Spring Boot 支持多种格式的配置文件,最常用的是 properties 格式(默认格式)和比较新颖的 yaml/yml 格式。下面将分别介绍这两种格式的特点。
properties
properties 格式是经典的键值对文本格式。也就是说,如果某一个配置文件的格式是 properties 格式,那么这个配置文件的文本格式为键值对。
键值对的语法非常简单,具体如下:
key=value
= 左侧为键(key),= 右侧为值(value)。
在配置文件中,每个键独占一行。如果多个键之间存在层级关系,就需要使用“父键.子健”的格式予以表示。
例如,在配置文件中,为一个有三层关系的键赋值的语法如下:
key1.key2.key3=value
例如,启动 Spring Boot 项目的 Tomcat 端口号为 8080,那么在这个项目的 application.properties 文件中就能够找到如下的内容:
server.port=8080
启动这个项目后,即可在控制台看到如下一行日志:
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
这行日志表明 Tomcat 根据配置开启的是 8080 端口。
在 application.properties 文件中,“#” 被称作注释符号,用于向文件中添加注释信息。例如:
# Tomcat端口
server.port=8080
yaml/yml
yml 是 YAML 的缩写,和 yaml 一样,它是一种可读性高的用于表达数据序列化的文本格式。yml 格式的配置文件的文本格式也是键值对。只不过,键值对的语法与 Python 语言中的键值对的语法非常相似,具体如下:
key: value
注意:
英文格式的 “:” 与值之间至少有一个空格。
英文格式的 “:” 左侧为键(key),英文格式的 “:” 右侧为值(value)。需要注意的是,英文格式的 “:” 与值之间只能用空格缩进,不能用 Tab 缩进;空格数量表示各层的层级关系。
例如,在配置文件中,为一个有三层关系的键赋值的语法如下:
key1:
key2:
key3: value
在 properties 格式的配置文件中,即使父键相同,在为每一个子健赋值时也要单独占一行,还要把父键写完整,例如:
com.spark.strudent.name=tom
com.spark.strudent.age=21
但是在 yaml 格式的配置文件中,只需要编写一次父键,并保证两个子健缩进相同即可。例如,把上述 properties 格式的键值对修改为 yaml 格式的键值对:
com:
spark:
student:
name: Tom
age: 21
对于 Spring Boot 项目的配置文件,不论是采用 properties 格式,还是采用 yaml 格式,都由程序开发人员自行决定。但是,在同一个 Spring Boot 项目中,尽量只使用一种格式的配置文件;否则,这个 Spring Boot 项目中的 yaml 格式的配置文件将被忽略掉。
1.2 达成约定的配置信息
虽然程序开发人员可以在配置文件中自定义配置信息,但是 Spring Boot 也有一些已经达成约定的配置信息。这些配置信息用于设置 Spring Boot 项目的一些属性,具体如下:
# Tomcat 使用的端口号
server.port=808080
# 配置 context-path
server.servlet.context-path=/
# 错误页地址
server.error.path=/error
# session 超时时间(分钟),默认为 30 分钟
server.servlet.session.timeout=60
# 服务器绑定的 IP 地址,如果本机不在此 IP 地址则启动失败
server.address=192.168.1.1
# Tomcat 最大线程数,默认为 200
server.tomcat.threads.max=100
# Tomcat 的 URI 字符编码
server.tomcat.uri-encoding=UTF-8
二、读取配置信息的值
程序开发人员如果已经在配置文件中保存了一些自定义的配置信息,那么在编码时应该如何读取这些配置信息的值呢?为此,Spring Boot 提供了 3 种读取方法。下面将对其分别予以介绍。
2.1 使用 @Value 注解读取值
@Value 注解可以向类的属性注入常量、Bean 或者配置文件中配置信息的值。使用 @Value 注解读取配置信息的值的语法下:
@Value("${key}")
例如,读取 Tomcat 开启的端口号,代码如下:
@Value("${server.port}")
Integer port;
我们在工程中测试一下:
- 在 application.properties 配置文件中添加:
com.spark.name=HanMeiMei
com.spark.age=18
com.spark.gender=\u5973
这 3 行内容分别对应:姓名、年龄和性别。
- 修改 BeanTestController:
@RestController
public class BeanTestController {
@Value("${com.spark.name}")
private String name;
@Value("${com.spark.age}")
private Integer age;
@Value("${com.spark.gender}")
private String gender;
@RequestMapping("/people")
public String getPeople() {
String result = "<li>名称:" + name + "</li>" +
"<li>名称:" + age + "</li>" +
"<li>名称:" + gender + "</li>";
System.out.println(gender);
System.out.println(gender);
System.out.println(gender);
return result;
}
}
- 运行,看看效果:
如果使用 @Value 读取一个不存在的配置信息的值,例如:
@Value("${com.mr.school}")
private String school;
那么,启动项目后将抛出如下的异常信息:
Could not resolve placeholder 'com.spark.school' in value "${com.spark.school}"
这个异常信息的含义是:程序无法找到与“com.mr.school”相匹配的值。因此,在使用@Value读取配置文件中的配置信息的值时,一定要确保配置信息的名称是存在的,并且是正确的。
2.2 使用 Environment 环境组件读取值
如果配置文件中的配置信息经常需要修改,那么为了能够读取配置信息的值,就需要使用一个更为灵活的方法,即 org.springframework.core.env.Environment 环境组件接口。这是因为即使 Environment 环境组件接口尝试读取一个不存在的配置信息的值,程序也不会抛出任何异常信息。
Environment 环境组件接口的对象是由 Spring Boot 自动创建的,程序开发人员可以直接注入并使用它。
Environment对象注入的方式如下:
@Autowired
Environment env;
Environment 环境组件接口提供了丰富的 API,下面列举几个常用的方法:
containsProperty(String key);
- key:配置文件中配置信息的名称。
- 返回值:如果配置文件存在名为 key 的配置信息,则返回 true,否则返回 false。
getProperty(String key)
- key:配置文件中配置信息的名称。
- 返回值:配置文件中 key 对应的值。如果配置文件中没有名为 key 的配置信息,则返回 null。
getProperty(String key, Class<T> targetType)
- key:配置文件中配置信息的名称。
- targetType:方法返回值封装成的类型。
- 返回值:配置文件中 key 对应的值,并封装成 targetType 类型。
getProperty(String key, String defaultValue);
- key:配置文件中配置信息的名称。
- defaultValue:默认值。
- 返回值:配置文件中 key 对应的值,如果配置文件中没有名为 key 的配置信息,则返回 defaultValue。
注意:
如果配置文件中存在某个配置信息,但等号右侧没有任何值(如 “name=”),那么 Environment 组件会认为这个配置信息存在,并且这个配置信息的值为空字符串。
我们看下在 BeanTestController 里面怎么使用,代码如下:
@RestController
public class BeanTestController {
@Autowired
Environment environment;
@RequestMapping("/environment")
public String getPeople() {
StringBuilder result = new StringBuilder();
if (environment.containsProperty("com.spark.name")) {
String name = environment.getProperty("com.spark.name");
result.append("<li>姓名:").append(name).append("</li>");
}
if (environment.containsProperty("com.spark.age")) {
int age = Integer.parseInt(Objects.requireNonNull(environment.getProperty("com.spark.age")));
result.append("<li>年龄:").append(age).append("</li>");
}
String school = environment.getProperty("com.spark.school", "XX 学院");
result.append("<li>学校:").append(school).append("</li>");
String language = environment.getProperty("com.spark.language", "C++");
result.append("<li>所学语言:").append(language).append("</li>");
return result.toString();
}
}
运行看下效果:
2.3 使用映射类的对象读取值
除了 @Value 注解和 Environment 环境组件,Spring Boot 还提供了用于声明映射类的 @ConfigurationProperties 注解。下面就对其予以介绍。
说明:
用于封装配置信息的类被称作映射类。