2.1 快速入门工程
第一章的 HelloBookController 控制层中,在代码中以硬编码的方式使用字符串表示书信息。下面把书的信息作为属性,外化配置在 application.properties 。好处是将应用参数、业务参数或第三方参数等统一配置在应用配置文件中,避免配置侵入业务代码,达到可配置的方式,方便及时调整修改。
2.1.1 配置属性
新建工程命名为 chapter-2-spring-boot-config ,在 application.properties 中配置书名和作者,配置如下:
## 书信息demo.book.name=[Spring Boot 2.x Core Action]demo.book.writer=BYSocket
.properties 文件的每行参数被存储为一对字符串,即一个存储参数名称,被称为键;另一个为值。一般称为键值对配置。井号(#)或者英文状态下的叹号(!)作为第一行中第一个非空字符来表示该行的文本为注释。另外,反斜杠(\)用于转义字符。
Spring Boot 支持并推荐使用 YAML 格式的配置文件,将 application.properties 文件替换成 application.yml 文件,并配置相同的属性,配置如下:
## 书信息demo: book: name:《SpringBoot2.x核心技术实战-上基础篇》 writer:泥瓦匠BYSocket
YAML 是一个可读性高,用来表达数据序列的格式。表示键值对格式时,注意键和值由冒号及空白字符分开。强调下,空白字符是必须的,IDE 一般也会提示。两种配置方式都非常便捷,在开发中选择 .properties 或 .yml 文件配置。但如果两种配置文件同时存在的时候,默认优先使用 .properties 配置文件。YAML 与 .properties 配置文件对比如图 2-1 所示:
图 2-1 YAML 与 .properties 配置文件对比
注意:
在 application.properties 配置中文值,读取时会出现中文乱码问题。因为 Java .properties 文件默认编码方式是 iso-8859 ,Spring Boot 应用以 UTF-8 的编码方式读取,就导致出现乱码问题。
官方 Issue 中的解决方法是,将 .properties 文件中配置的中文值转义成 Unicode 编码形式。例如 demo.book.writer=泥瓦匠 应该配置成 demo.book.writer=\u6ce5\u74e6\u5320 。利用 IDEA properties 插件 或利用 Java 文件转码工具 native2ascii 来快速地进行转义。该工具有在线版实现,地址如下:
https://javawind.net/tools/native2ascii.jsp
2.1.2 创建属性类
在工程中新建包目录 demo.springboot.config ,并在目录中创建名为 BookProperties 的属性类,代码如下:
importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;/**
* 书属性
*/@ComponentpublicclassBookProperties{/** * 书名 */@Value("${demo.book.name}") privateStringname;/** * 作者 */@Value("${demo.book.writer}") privateStringwriter;// ... 省略 getter / setter 方法}
利用 @Component 注解定义了书的属性 Bean,并通过 @Value 注解为该 Bean 的成员变量(或者方法参数)自动注入 application.properties 文件的属性值。@Value 注解是通过 “${propName}” 的形式引用属性,propName 表示属性名称。
核心注解的知识点:
@Component 注解:
@Component 对类进行标注,职责是泛指组件 Bean ,应用启动时会被容器加载并加入容器管理。常见的 @Controller、@Service 、@Repository 是 @Component 的分类细化组件,分别对应控制层、服务层、持久层的 Bean。
@Value 注解:
@Value 对 Bean 的字段或者方法参数进行标注,职责是基于表达式给字段或方法参数设置默认属性值。通常格式是注解 + SpEL 表达式,如 @Value("SpEL 表达式")。
使用 @Vlaue 注解来引用属性值时,确保所引用的属性值在 application.properties 文件存在并且相对应匹配,否则会造成 Bean 的创建错误,引发 java.lang.IllegalArgumentException 非法参数异常。
2.1.3 获取属性
修改原有的 HelloBookController 类,通过注入的方式获取书属性 Bean 并返回。代码如下:
importdemo.springboot.config.BookProperties;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloBookController { @AutowiredBookProperties bookProperties; @GetMapping("/book/hello") public String sayHello() {return"Hello, " +bookProperties.getWriter() + "iswriting" +bookProperties.getName() + " !"; }}
通过 @Autowired 注解标记在 BookProperties 字段,控制层自动装配属性 Bean 并使用。默认情况下要求被注解的 Bean 必须存在,需要允许 NULL 值,可以设置其 required 属性为 false: @Autowired(required = false)。
2.1.4 运行工程
执行 ConfigApplication 类启动,在控制台看到成功运行的输出后,打开浏览器访问 /book/hello 地址,可以看到如图 2-2 所示的返回结果:
正在上传... 取消
图 2-2 Hello Book 页面
也可以通过单元测试的方式验证属性获取是否成功,单元测试具体相关的会在第 9 章节介绍。单元测试代码如下:
importdemo.springboot.config.BookProperties;importorg.junit.Assert;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublicclassConfigApplicationTests{@AutowiredBookPropertiesbookProperties;@Testpublicvoid testBookProperties() {Assert.assertEquals(bookProperties.getName(),"'Spring Boot 2.x Core Action'");Assert.assertEquals(bookProperties.getWriter(),"BYSocket");}}
2.2 配置属性的获取方式
配置属性的常用获取方式有基于 @Value 和 @ConfigurationProperties 注解两种方式。两种方式适合的场景不同,下面具体介绍其使用方法和场景。
2.2.1 @Value 注解
@Value 注解对 Bean 的变量或者方法参数进行标注,职责是基于表达式给字段或方法参数设置默认属性值。通常格式是注解 + SpEL 表达式,如 @Value("SpEL 表达式"),并标注在对应的字段或者方法上方,且必须对变量一一标注。这种方式适用于小而不复杂的属性结构。属性结构复杂,字段很多的情况下,这种方式会比较繁琐,应该考虑使用 @ConfigurationProperties 注解。
另外通过 @PropertySource 注解引入对应路径的其他 .properties 文件。将书信息重新配置在 classpath 下新的 book.properties 配置文件后,读取新配置文件的代码如下:
importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.PropertySource;importorg.springframework.stereotype.Component;/**
* 书属性
*/@Component@PropertySource("classpath:book.properties")publicclassBookProperties{/** * 书名 */@Value("${demo.book.name}") privateStringname;/** * 作者 */@Value("${demo.book.writer}") privateStringwriter;// ... 省略 getters / setters 方法}
2.2.2 @ConfigurationProperties 注解
在包目录 demo.springboot.config 中创建名为 BookComponent 的属性类,并使用 @ConfigurationProperties 注解获取属性,代码如下:
importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;/**
* 书属性
*
*/@Component@ConfigurationProperties(prefix="demo.book")publicclassBookComponent{/**
* 书名
*/privateStringname;/**
* 作者
*/privateStringwriter;// ... 省略 getters / setters 方法}
类似 @Value 注解方式,使用 @ConfigurationProperties(prefix = "demo.book") 注解标注在类上方可以达到相同的效果。 @ConfigurationProperties 注解的 prefix 是指定属性的参数名称。会匹配到配置文件中 “ demo.book.* ” 结构的属性,星号 “ * ” 是指会一一对应匹配 BookComponent 类的字段名。例如,字段 name 表示书名,会匹配到 demo.book.name 属性值。
@Value 注解方式强制字段必须对应在配置文件, @ConfigurationProperties 注解方式则不是必须的。一般情况下,所有字段应该保证一一对应在配置文件。如果没有属性值对应的话,该字段默认为空, @ConfigurationProperties 注解方式也不会引发任何异常,Spring Boot 推荐使用 @ConfigurationProperties 注解方式获取属性。
同样使用单元测试验证获取属性是否成功。单元测试代码如下:
@AutowiredBookComponent bookComponent;@Testpublic void testBookComponent() {Assert.assertEquals(bookComponent.getName(),"'Spring Boot 2.x Core Action'");Assert.assertEquals(bookComponent.getWriter(),"BYSocket");}
API org.springframework.boot.context.properties.ConfigurationProperties 注解参数
prefix
字符串值,绑定该名称前缀的属性对象。
value
字符串值,功能同 prefix 参数。
ignoreInvalidFields
布尔值,默认 false。绑定对象时,忽略无效字段。
ignoreUnknownFields
布尔值,默认 true。绑定对象时,忽略未知字段。
2.2.3 @ConfigurationProperties 数据验证
@ConfigurationProperties 注解方式支持验证功能,即当属性类被 @Validated 注解标注时,Spring Boot 初始化时会验证类的字段。在类的字段上添加 JSR-303 约束注解,进行数据验证。下面为书属性字段添加非 NULL 和字符串非空约束,代码如下:
importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;importorg.springframework.validation.annotation.Validated;importjavax.validation.constraints.NotEmpty;importjavax.validation.constraints.NotNull;/**
* 书属性
*
*/@Component@ConfigurationProperties(prefix="demo.book")@ValidatedpublicclassBookComponent{/**
* 书名
*/@NotEmptyprivateStringname;/**
* 作者
*/@NotNullprivateStringwriter;// ... 省略 getters / setters 方法}
龙华大道1号http://www.kinghill.cn/Dynamics/2106.html