SpringBoot系列三:SpringBoot自定義Starter
在前面兩章SpringBoot入門 、 ofollow,noindex" target="_blank">SpringBoot自動配置原理 的學習後,我們對如何建立一個 SpringBoot 專案、SpringBoot 的執行原理以及自動配置等都有了一定的瞭解。如果我們系統中也想要擁有自動配置的功能,可以自己編寫一個starter (啟動器),想想就覺得很酷,因為這意味著我們不僅有自己定義的自動配的功能,而且具有更通用的耦合度更低的配置。
還是以第一章開頭的簡單功能為例:瀏覽器傳送 sayHello 請求,伺服器接受請求並處理,響應 Hello 。
首先我們看下工程結構:
helloworld-spring-boot-starter-autoconfigure(以下簡稱autoconfigure):該模組用來實現 Helloworld 的自動配置功能,它的打包方式為 jar;
helloworld-spring-boot-starter(以下簡稱starter):該模組的打包方式是 jar,依賴 autoconfigure 模組,它不寫任何程式碼,只做自動配置包的自動引入,如果要使用 helloworld 自動配置功能,只要在 pom 檔案中引入 starter 模組即可:
<dependency> <groupId>com.seagetech.spring.boot</groupId> <artifactId>helloworld-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
1 專案工程的建立
1.1 建立Empty Project
1) 新建一個空的專案,如下圖,點選Next
2) 輸入專案名稱以及選擇專案存放路徑,點選Finish
1.2 建立starter模組
1) 在1.1節中新建的空專案基礎上新建一個Module
2) 選擇Maven,點選Next
3) 輸入GroupId、ArtifactId 和 Version 資訊,點選Finish
4) 由於這個模組只做自動配置包的引入,所有刪除 src 下的包,最終專案結構如下:
pom.xml 檔案:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.seagetech.spring.boot</groupId> <artifactId>helloworld-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </project>
1.3 建立 autoconfigure 模組
1) 按1.2節第1步,新建一個 Module,並選擇 Spring Initializr,點選Next
2) 輸入專案相關資訊,如下圖所示:
3) 選擇依賴,我們這裡只選擇web場景的依賴,點選Next
4) 預設,不需要改動,點選Finish
4) 刪除 SpringBoot 自動建立的主配置類、resources 下所有檔案(夾)以及 test 資料夾,最終專案結構如下:
pom.xml(已刪除 test 測試模組的自動配置包以及 build 下的 Maven 外掛)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.seagetech.spring.boot</groupId> <artifactId>helloworld-spring-boot-starter-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>helloworld-spring-boot-starter-autoconfigure</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
5) 在1.2節建立的 helloworld-spring-boot-starter 的 pom 下模組引入本節建立的 autoconfigure 模組:
<dependency> <groupId>com.seagetech.spring.boot</groupId> <artifactId>helloworld-spring-boot-starter-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
1.4 實現HelloWorld自動配置
1) 在 helloworld 包下新建 HelloWorldProperties 類,作為配置類,目的是後面使用這個自動配置,直接可以在 application.properties 檔案中配置:
package com.seagetech.spring.boot.helloworld; import org.springframework.boot.context.properties.ConfigurationProperties; /** * HelloWorld屬性配置類 * @auther: wangzb * @date: 2018/11/13 22:21 */ @ConfigurationProperties(prefix = "hello") public class HelloWorldProperties { /** * 打招呼的內容,預設為“World!” */ private String msg = "World!"; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
2) 新建 HelloWorldService 介面,並實現:
介面:
/** * @auther: wangzb * @date: 2018/11/13 22:28 */ public interface HelloWorldService { /** * 打招呼方法 * @param name 人名,向誰打招呼使用 * @return */ String sayHello(String name); }
實現類:
/** * @auther: wangzb * @date: 2018/11/13 22:33 */ @Component public class HelloWorldServiceImpl implements HelloWorldService{ @Autowired private HelloWorldProperties helloWorldProperties; /** * 打招呼方法 * * @param name 人名,向誰打招呼使用 * @return */ @Override public String sayHello(String name) { return "Hello " + name + " " + helloWorldProperties.getMsg(); } }
3) 新建 HelloWorldAutoConfiguration 配置類
/** * @auther: wangzb * @date: 2018/11/13 22:37 */ //定義為配置類 @Configuration //在web條件下成立 @ConditionalOnWebApplication //啟用HelloWorldProperties配置功能,並加入到IOC/">IOC容器中 @EnableConfigurationProperties({HelloWorldProperties.class}) //匯入HelloWorldService元件 @Import(HelloWorldServiceImpl.class) public class HelloWorldAutoConfiguration { }
1.5 建立 spring.factories 檔案
在 SpringBoot自動配置原理 中,我們提到 @EnableAutoConfiguration 的關鍵功能是使用 SpringFactoriesLoader.loadFactoryNames 方法來掃描具有 META-INF/spring.factories 檔案的 jar 包,這樣我們的自動配置類才能生效,所以我們在 autoconfigure 模組的 resources 下建立 META-INF/spring.factories 檔案:
在 spring.factories 中寫入:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.seagetech.spring.boot.helloworld.HelloWorldAutoConfiguration
2 helloworld 自動配置的使用
建立好 helloworld 自動配置專案後,接下來就是使用了,我們在 I dea 中使用 install 將1節中建立好的 starter/autoconfigure 模組打包到本地 M aven 創庫,然後在 SpringBoot入門 章節建立的 sping-boot-demo 專案中使用,在其 pom 檔案中引入:
<dependency> <groupId>com.seagetech.spring.boot</groupId> <artifactId>helloworld-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
在主配置類路徑下建立 HelloWorldController :
package com.seagetech.springbootdemo; import com.seagetech.spring.boot.helloworld.HelloWorldService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @auther: wangzb * @date: 2018/11/17 11:17 */ @Controller public class HelloWorldController { @Autowired private HelloWorldService helloWorldService; @RequestMapping(value = "/sayHello") public String sayHello(String name){ return helloWorldService.sayHello(name); } }
啟動專案,在瀏覽器輸入: http://localhost:8080/sayHello?name=wangzb ,瀏覽器返回:
hello.msg = 你好
我們在重啟專案,重複以上步驟,瀏覽器響應:
Hello wangzb 你好
3 元資料的配置
到目前為止,helloworld 的自動配置功能已實現,並且正確使用了,但還有一點不夠完美,如果你也按上面步驟實現了自己的 helloworld 自動配置,在 application.properties 中配置 hello.msg 屬性時,你會發現並沒有提示你有關該配置的資訊,但是如果你想配置 Tomcat 埠時,輸入 server.port 是有提示的:
這種功能我們如何做呢,我們開啟 SpringBoot入門 章節下載的 “spring-boot-reference.pdf” 檔案,在目錄中找到 “Appendix B. Configuration Metadata(附錄B.配置元資料)”,下面我們使用谷歌來翻譯下相關原文:
Spring Boot jars include metadata files that provide details of all supported configuration properties.The files are designed to let IDE developers offer contextual help and “code completion” as users are working with application.properties or application.yml files.
Spring Boot jar 包含元資料檔案,提供所有支援的配置屬性的詳細資訊。這些檔案旨在讓 IDE 開發人員使用 application.properties或 application.yml 檔案像使用者一樣提供上下文幫助和“程式碼完成”
……
B.1 Metadata Format
Configuration metadata files are located inside jars under META-INF/spring-configurationmetadata.json They use a simple JSON format with items categorized under either “groups” or “properties” and additional values hints categorized under “hints”, as shown in the following example:
B.1 元資料格式
配置元資料檔案位於 META-INF / spring-configuration-metadata.json下的 jar 中。它們使用簡單的 JSON 格式,其中的專案分類為 “groups” 或 “properties” 和其他值提示分類在 “hints” 下。
….
看看它的例子:
{"groups": [{ "name": "server", "type": "org.springframework.boot.autoconfigure.web.ServerProperties", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" }, ... ], "properties": [{ "name": "server.port", "type": "java.lang.Integer", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" },{ "name": "server.address", "type": "java.net.InetAddress", "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties" }, ... ], "hints": [{ "name": "spring.jpa.hibernate.ddl-auto", "values": [{ "value": "none", "description": "Disable DDL handling." },{ "value": "validate", "description": "Validate the schema, make no changes to the database." },{ "value": "update", "description": "Update the schema if necessary." }] ]}
所以元資料是在位於 META-INF/spring-configuration-metadata.json 下配置的,下面就列出有關 groups、properties、hints 使用。
3.1 Group屬性
“groups” 中包含的 JSON 物件可以包含下表中顯示的屬性:
3.2 Property屬性
properties 陣列中包含的 JSON 物件可由以下屬性構成:
3.3 hints屬性
hints 陣列中包含的 JSON 物件可以包含以下屬性:
每個 “hints” 元素的 values 屬性中包含的 JSON 物件可以包含下表中描述的屬性:
每個 “hints” 元素的 providers 屬性中的 JSON 物件可以包含下表中描述的屬性:
3.4 配置元資料
所以如果想有更好的使用者體驗,可以為我們自定義的 starter 配置元資料,下面就為 HelloWorld 配置元資料。
在 META-INF 下建立 spring-configuration-metadata.json 檔案:
在檔案中輸入如下內容:
{ "hints":[{ "name":"hello.msg", "values":[{ "value":"你好", "description":"中文方式打招呼" },{ "value":"Hi", "description":"英文方式打招呼" }] }], "groups":[ { "sourceType": "com.seagetech.spring.boot.helloworld.HelloWorldProperties", "name": "hello", "type": "com.seagetech.spring.boot.helloworld.HelloWorldProperties" }], "properties":[ { "sourceType": "com.seagetech.spring.boot.helloworld.HelloWorldProperties", "name": "hello.msg", "type": "java.lang.String", "description": "打招呼的內容", "defaultValue": "Worlds" }] }
然後將 autoconfigure 模組重新打包,接下來就是在 spring-boot-demo 專案中使用,如下圖所示,就有了屬性的提示: