Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343