如何手寫一個Spring Boot Starter
想必大家都使用過 SpringBoot,在 SpringBoot 項目中,使用最多的無非就是各種各樣的 Starter 了。那何為 Starter 呢?你可以理解為一個可拔插式的插件(組件)。或者理解為場景啟動器。
通過 Starter,能夠簡化以前繁雜的配置,無需過多的配置和依賴,它會幫你合并依賴,并且將其統一集成到一個 Starter 中,我們只需在 Maven 或 Gradle 中引入 Starter 依賴即可。SpringBoot 會自動掃描需要加載的信息并啟動相應的默認配置。例如,如果你想使用 jdbc 插件,你只需引入 spring-boot-starter-jdbc 即可;如果你想使用 mongodb,你只需引入 spring-boot-starter-data-mongodb 依賴即可。
SpringBoot 官方提供了大量日常企業應用研發各種場景的 spring-boot-starter 依賴模塊。這些依賴模塊都遵循著約定成俗的默認配置,并允許我們根據自身情況調整這些配置。
總而言之,Starter 提供了以下功能:
整合了模塊需要的所有依賴,統一集合到 Starter 中。 提供了默認配置,并允許我們調整這些默認配置。 提供了自動配置類對模塊內的 Bean 進行自動裝配,注入 Spring 容器中。 Starter 命名規則Spring 官方定義的 Starter 通常命名遵循的格式為 spring-boot-starter-{name},例如 spring-boot-starter-data-mongodb。Spring 官方建議,非官方 Starter 命名應遵循 {name}-spring-boot-starter 的格式,例如,myjson-spring-boot-starter。
自定義一個 Starter了解了 Starter 的含義以及應用場景后,我們可以嘗試手寫一個 Starter,加深對它的了解以及能在實際工作中,開發出自己的 Starter,提高我們的開發效率。
可能有人會問 Starter 能干嘛呢?其實在我們的日常開發工作中,總有一些獨立于業務系統之外的配置模塊,它是可以在不同項目中進行復用的。如果在每個項目中都編寫重復的模塊代碼,不僅浪費時間和人力,而且還和項目耦合。所以我們將這些可獨立于業務代碼之外的功能配置模塊封裝成一個 Starter,在需要用到此功能模塊的項目中,只需要在其 pom.xml 文件中引用依賴即可,SpringBoot 幫我們完成自動裝配,而且我們還可以在配置文件中調整 Starter 中默認的配置信息。
假設我們現在需要實現這樣一個功能:
根據用戶提供的 Java 對象,將其轉換為 JSON 形式,并且在 JSON 字符串中添加指定的前輟和后輟。 用戶可以動態改變前輟和后輟,即可在 yml 或 properties 配置文件中自定義。舉個栗子,假如用戶輸入下面這個類的對象 person:
public class Person { private String name; private int age; private String address; public Person(String name, int age, String address) { super(); this.name = name; this.age = age; this.address = address; } // 省略get和set方法}
Person person = new Person('Mr.nobody', 18, '拉斯維加斯');
并假設用戶在 application.yml 配置文件中配置的前輟為 @,后輟為 %,則最終生成的字符串為:
@{'address':'拉斯維加斯','age':18,'name':'Mr.nobody'}%
首先新建一個 Maven 工程(當然也可以其他類型例如 Gradle 工程),在 pom.xml 文件中引入如下依賴。fastjson 依賴是我們業務用到將 Java 對象轉換為 JSON 字符串;spring-boot-configuration-processor 依賴是可選的,加入此依賴主要是打包時,自動生成配置元信息文件 META-INF/spring-configuration-metadata.json,并放入到 jar 中。方便使用者了解到一些配置元信息。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd'><modelVersion>4.0.0</modelVersion><groupId>com.nobody</groupId><artifactId>myjson-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><name>myjson-spring-boot-starter</name><description>Demo project for Spring Boot Starter</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.3.8.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><version>2.3.8.RELEASE</version><optional>true</optional></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.3.8.RELEASE</version></dependency></dependencies></project>
業務處理類,實現 Java 對象轉換為帶有指定前后綴的 JSON 字符串。
package com.nobody.myjson.service;import com.alibaba.fastjson.JSON;/** * @Description 業務處理類 * @Author Mr.nobody * @Date 2021/2/27 * @Version 1.0 */public class MyJsonService { // 前綴 private String prefixName; // 后綴 private String suffixName; /** * 將Java對象轉為帶有指定前后綴的JSON字符串 * * @param o 需要轉換的Java對象 * @return 轉換后的字符串 */ public String objectToMyJson(Object o) { return prefixName + JSON.toJSONString(o) + suffixName; } public String getPrefixName() { return prefixName; } public void setPrefixName(String prefixName) { this.prefixName = prefixName; } public String getSuffixName() { return suffixName; } public void setSuffixName(String suffixName) { this.suffixName = suffixName; }}配置類,定義需要的配置信息和默認配置項,并指明關聯配置文件的配置項前綴。它可以把相同前綴的配置信息通過配置項名稱映射成實體類的屬性中。package com.nobody.myjson.config;import org.springframework.boot.context.properties.ConfigurationProperties;/** * @Description 配置類(類名一般為模塊名+Properties) nobody.json為Starter使用者通過yml配置文件動態修改屬性值的變量名前綴 * @Author Mr.nobody * @Date 2021/2/27 * @Version 1.0 */@ConfigurationProperties(prefix = 'nobody.json')public class MyJsonProperties { // Starter使用者沒在配置文件中配置prefixName屬性的值時的默認值 public static final String DEFAULT_PREFIX_NAME = '@'; // Starter使用者沒在配置文件中配置suffixName屬性的值時的默認值 public static final String DEFAULT_SUFFIX_NAME = '@'; private String prefixName = DEFAULT_PREFIX_NAME; private String suffixName = DEFAULT_SUFFIX_NAME; public String getPrefixName() { return prefixName; } public void setPrefixName(String prefixName) { this.prefixName = prefixName; } public String getSuffixName() { return suffixName; } public void setSuffixName(String suffixName) { this.suffixName = suffixName; }}
自動裝配類,使用 @Configuration 和 @Bean 來進行自動裝配,注入 Spring 容器中。
package com.nobody.myjson.config;import com.nobody.myjson.service.MyJsonService;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @Description 自動裝配類 * @Author Mr.nobody * @Date 2021/2/27 * @Version 1.0 */@Configuration // 標識此類是配置類@ConditionalOnClass(MyJsonService.class) // 表示只有指定的class在classpath上時才能被注冊@EnableConfigurationProperties(MyJsonProperties.class) // 激活@ConfigurationPropertiespublic class MyJsonConfiguration { private MyJsonProperties myJsonProperties; // 自動注入配置類 public MyJsonConfiguration(MyJsonProperties myJsonProperties) { this.myJsonProperties = myJsonProperties; } // 創建MyJsonService對象,注入到Spring容器中 @Bean @ConditionalOnMissingBean(MyJsonService.class) // 當容器沒有此bean時,才注冊 public MyJsonService myJsonService() { MyJsonService myJsonService = new MyJsonService(); myJsonService.setPrefixName(myJsonProperties.getPrefixName()); myJsonService.setSuffixName(myJsonProperties.getSuffixName()); return myJsonService; }}
在 src/main/resources/META-INF目錄下新建 spring.factories 文件,輸入以下內容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.nobody.myjson.config.MyJsonConfiguration
SpringBoot 項目啟動時,類加載器會從 META-INF / spring.factories 加載給定類型的工廠實現的完全限定類名。也就是說類加載器得到工程中所有 jar 包中的 META-INF/spring.factories 文件資源,從而得到了一些包括自動配置相關的類的集合,然后將它們實例化,放入 Spring 容器中。
最終項目結構如下:
在開發工具 IDEA 通過 Maven 的 install 命令進行構建打包。或者在項目的目錄下,打開命令行窗口,使用mvn install命令進行構建打包。打包后,會在工程的 target 目錄下生成一個 jar 包,并且在 maven 本地倉庫也會生成相應的 jar 包。
經過上面幾個步驟,我們自定義的 Starter 就開發好了,以下是在其他工程進行引入使用。在需要引用此 Starter 的工程的 pom.xml 文件中引入此依賴。
<dependency> <groupId>com.nobody</groupId> <artifactId>myjson-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version></dependency>
刷新依賴,就能在項目的依賴庫中看到此依賴了。
展開,還能查看此 Starter 可以配置的屬性項有哪些,如下:
然后在需要用到的類中進行注入使用即可。
package com.nobody.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.nobody.domain.Person;import com.nobody.service.MyJsonService;@RestController@RequestMapping('demo')public class DemoController { // 注入我們Starter中的服務類 @Autowired private MyJsonService myJsonService; @GetMapping() public String test() { Person p = new Person('Mr.nobody', 18, '拉斯維加斯'); // 調用服務方法 return myJsonService.objectToMyJson(p); }}
啟動項目,在瀏覽器中訪問此接口,得到如下結果:
如果我們在 application.yml 文件中添加以下配置信息,然后再訪問接口的結果如下,也驗證了我們可以自定義 Starter 中默認的配置項。
nobody: json: prefixName: HH suffixName: KK
當我們引入此 Starter 時,SpringBoot 會自動裝配,將實例化的 bean 放入 Spring 容器。但我們是否可控制 bean 要不要實例化并放入容器呢?答案是可以做到的。
我們只需要在自動裝配類或者類內的方法,通過 @ConditionalOnXXX 注解就能控制。例如如下所示,使用 Starter 使用者在他的項目的配置文件中填寫 nobody.json.enable 的值為 false,則就不會自動生成 MyJsonService 實例了。默認不填或者 nobody.json.enable 的值為 true 時,能自動生成 bean 放入容器。這樣用戶就能自己控制 bean 的實例化了。
// 創建MyJsonService對象,注入到Spring容器中@Bean@ConditionalOnProperty(name = 'nobody.json.enable', matchIfMissing = true)@ConditionalOnMissingBean(MyJsonService.class) // 當容器沒有此bean時,才注冊public MyJsonService myJsonService() { MyJsonService myJsonService = new MyJsonService(); myJsonService.setPrefixName(myJsonProperties.getPrefixName()); myJsonService.setSuffixName(myJsonProperties.getSuffixName()); return myJsonService;}
此演示項目已上傳到Github,如有需要可自行下載,歡迎 Star 。
https://github.com/LucioChn/myjson-spring-boot-starter
以上就是如何手寫一個Spring Boot Starter的詳細內容,更多關于手寫Spring Boot Starter的資料請關注好吧啦網其它相關文章!
相關文章:
