手把手教你實現自定義Spring Boot的 Starter
阿新 • • 發佈:2021-01-28
### 引言
上篇文章[《天天用SpringBoot,它的自動裝配原理卻說不出來》](https://mp.weixin.qq.com/s/TnofWzuaH-WDcfYVfp-UoA)我們有說`springBoot`的自動裝配怎麼實現的(建議最好先看下篇文章,因為前後有關係),這篇文章的話我們就自己來實現一個`SpringBoot`的 `starter`吧。廢話不多說我們還是直入主題吧。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200802003847983.png)
什麼是`Spring Boot Starter`呢?我們直接來看看官網是怎麼介紹的吧。
>Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and related technologies that you need without having to hunt through sample code and copy-paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, include the spring-boot-starter-data-jpa dependency in your project.
>
納尼,一大堆的英文,這還有興趣接著往下看嗎?是不是看到這直接退出了。都到門口了,不進來喝杯茶再走嘛?看都看到這了還是接著繼續往下看吧。我們先不解釋這一段話是什麼意思,我們可以看看`starter`的出現給我們解決了什麼問題。
我們還是以上述官網的例子來進行說明比如說我們需要在`Spring` 中適應`JPA`來操作資料庫。
在沒有`springBoot-starter`之前,我們需要引入`jpa`的步驟
- 通過`maven` 引入jdbc的依賴、以及jpa相關的各種依賴
- 編寫`jpa`相關的配置檔案
- 網上各種查詢找資料進行除錯,除錯的過程對於新手可能會有點奔潰會遇到各種奇奇怪怪的問題,`jar`包衝突啊,這個`jar`包下載不下來,缺少某個`jar`包。
- 終於在經歷千辛萬苦,哼次哼次的解決各種問題之後終於把專案跑起來了,然後把這次整合`jpa`遇到的問題,以及整合的步驟都一一的詳細記錄下來。方便下次在需要整合`jpa`的時候直接`copy`就好了。
我們以前在沒有`starter`之前是不是都是這麼玩的。這樣的缺點是不是也非常顯著,比如過程複雜、需要不停的貼上複製(不過這是程式設計師經常乾的事情了,也不在乎多一兩次了)、整合其它元件到自己的專案變的困難,效率低下。這也就造成了`996`的程式設計師比較多了(晚上就不能夠回去`69`了)。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200801235345500.png)
### SpringBoot Starter的出現
我們可以看下`SpringBoot` 現在都為我們提供有哪些`starter`,我這邊這截圖了部分`starter`,更多的請點選https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200729151737421.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3plbmdmYW53ZWkxOTkw,size_16,color_FFFFFF,t_70)
`starter`的實現:雖然我們每個元件的`starter`實現各有差異,但是它們基本上都會使用到兩個相同的內容:`ConfigurationProperties`和`AutoConfiguration`。因為`Spring Boot`提倡“**約定大於配置**”這一理念,所以我們使用`ConfigurationProperties`來儲存我們的配置,並且這些配置都可以有一個預設值,即在我們沒有主動覆寫原始配置的情況下,預設值就會生效。除此之外,`starter`的`ConfigurationProperties`還使得所有的配置屬性被聚集到一個檔案中(一般在`resources`目錄下的`application.properties`),這樣我們就告別了`Spring`專案中`XML`地獄。
`starter`的出現幫把我們把各種複雜的配置都封裝起來了,讓我們真正的可以達到了開箱即用。不僅降低了我們使用它的門檻,並且還大大提高了我們的開發效率。正如前面所說[《SpringBoot自動裝配》](https://mp.weixin.qq.com/s/TnofWzuaH-WDcfYVfp-UoA)讓我們有更多的時間去陪女朋友。
### 實現自己的SpringBoot Starter
#### 命名規範
如果你快有孩子了,出生前你比較急的一定是起個名字。孩子的姓名標識著你和你愛人的血統,一定不會起隔壁老王的姓氏,肯定會招來異樣的眼光。在`maven`中,`groupId`代表著姓氏,`artifactId`代表著名字。`Spring Boot`也是有一個命名的建議的。所以名字是不能夠隨隨便便取得,可以按照官方的建議來取。
>What’s in a name
All official starters follow a similar naming pattern; spring-boot-starter-*, where * is a particular type of application. This naming structure is intended to help when you need to find a starter. The Maven integration in many IDEs lets you search dependencies by name. For example, with the appropriate Eclipse or STS plugin installed, you can press ctrl-space in the POM editor and type “spring-boot-starter” for a complete list.
As explained in the “Creating Your Own Starter” section, third party starters should not start with spring-boot, as it is reserved for official Spring Boot artifacts. Rather, a third-party starter typically starts with the name of the project. For example, a third-party starter project called thirdpartyproject would typically be named thirdpartyproject-spring-boot-starter.
大概意思是
官方的 `starter` 的命名格式為 `spring-boot-starter-{xxxx}` 比如`spring-boot-starter-activemq`
第三方我們自己的命名格式為 `{xxxx}-spring-boot-starter`。比如`mybatis-spring-boot-starter`。
如果我們忽略這種約定,是不是會顯得我們寫的東西不夠“專業“。
#### 自定義一個Starter
下面我們就來實現一個自定義的傳送簡訊的starter,命名為`sms-spring-boot-starter`。
1. **引入`pom`**
```xml
```
2. **編寫配置檔案**
發簡訊我們需要配置一些賬號資訊,不同的簡訊供應商,賬戶資訊是不一樣的,所以我們需要定義一`個XXXXProperties` 來自動裝配這些賬戶資訊。下面我們就以騰訊雲和阿里雲兩家供應商為例;
```java
@ConfigurationProperties(prefix = "sms")
@Data
public class SmsProperties {
private SmsMessage aliyun = new SmsMessage();
private SmsMessage tencent = new SmsMessage();
@Data
public static class SmsMessage{
/**
* 使用者名稱
*/
private String userName;
/**
* 密碼
*/
private String passWord;
/**
* 祕鑰
*/
private String sign;
/**
*
*/
private String url;
@Override
public String toString() {
return "SmsMessage{" +
"userName='" + userName + '\'' +
", passWord='" + passWord + '\'' +
", sign='" + sign + '\'' +
", url='" + url + '\'' +
'}';
}
}
}
```
如果需要在其他專案中使用傳送簡訊功能的話,我們只需要在配置檔案(`application.yml`)中配置`SmsProperties` 的屬性資訊就可以了。 比如:
```java
sms:
aliyun:
pass-word: 12345
user-name: java金融
sign: 阿里雲
url: http://aliyun.com/send
tencent:
pass-word: 6666
user-name: java金融
sign: 騰訊雲
url: http://tencent.com/send
```
還記的`@ConfigurationProperties`註解裡面是不是有個`prefix` 屬性,我們配置的這個屬性是`sms`,配置這個的主要一個作用的話是主要用來區別各個元件的引數。這裡有個小知識點需要注意下當我們在配置檔案輸入`sms`我們的`idea`會提示這個`sms`有哪些屬性可以配置,以及每個屬性的註釋都有標記,建議的話註釋還是寫英文,這樣會顯得你比較專業。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200731152320326.png)
這個提示的話,是需要引入下面這個`jar`的。
```xml
```
引入這個`jar`之後,我們編譯之後就會在`META-INF`資料夾下面生成一個`spring-configuration-metadata.json`的檔案。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200731153010961.png)
我們可以看到這個檔案其實 是根據SmsProperties類的成員屬性來生成的。
3. **然後在編寫簡訊自動配置類:**
```java
@EnableConfigurationProperties(value = SmsProperties.class)
@Configuration
public class SmsAutoConfiguration {
/**
* 阿里雲傳送簡訊的實現類
* @param smsProperties
* @return
*/
@Bean
public AliyunSmsSenderImpl aliYunSmsSender(SmsProperties smsProperties){
return new AliyunSmsSenderImpl(smsProperties.getAliyun());
}
/**
* 騰訊雲傳送簡訊的實現類
* @param smsProperties
* @return
*/
@Bean
public TencentSmsSenderImpl tencentSmsSender(SmsProperties smsProperties){
return new TencentSmsSenderImpl(smsProperties.getTencent());
}
}
```
編寫我們的傳送簡訊實現類:
```java
public class AliyunSmsSenderImpl implements SmsSender {
private SmsMessage smsMessage;
public AliyunSmsSenderImpl(SmsMessage smsProperties) {
this.smsMessage = smsProperties;
}
@Override
public boolean send(String message) {
System.out.println(smsMessage.toString()+"開始傳送簡訊==》簡訊內容:"+message);
return true;
}
}
```
4. **讓starter生效**
`starter`整合應用有兩種方式:
- 被動生效
我們首先來看下我們熟悉的方式,通過`SpringBoot`的`SPI`的機制來去載入我們的starter。我們需要在`META-INF`下新建一個`spring.factories`檔案`key`為`org.springframework.boot.autoconfigure.EnableAutoConfiguration, value`是我們的`SmsAutoConfiguration` 全限定名(**記得去除前後的空格,否則會不生效**)。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020073116194076.png)
- 主動生效
在`starter`元件整合到我們的`Spring Boot`應用時需要主動宣告啟用該`starter`才生效,通過自定義一個`@Enable`註解然後在把自動配置類通過`Import`註解引入進來。
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({SmsAutoConfiguration.class})
public @interface EnableSms {
}
```
使用的時候需要在啟動類上面開啟這個註解。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200731164241293.png)
5.**打包,部署到倉庫**
如果是本地的話,直接通過`mvn install`命令就可以了。
如果需要部署到公司的倉庫話,這個就不說了。
6. **新建一個新的`SpringBoot`專案引入我們剛寫的`starter`**
```java
```
在專案配置檔案配上簡訊賬號資訊
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200731173654747.png)
測試程式碼
```java
@SpringBootApplication
@EnableSms
public class AutoconfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(AutoconfigApplication.class, args);
AliyunSmsSenderImpl aliyunSmsSender = applicationContext.getBean(AliyunSmsSenderImpl.class);
aliyunSmsSender.send("用阿里雲傳送簡訊");
TencentSmsSenderImpl tencentSmsSender = applicationContext.getBean(TencentSmsSenderImpl.class);
tencentSmsSender.send("用騰訊雲傳送簡訊");
}
```
執行結果:
```java
SmsMessage{userName='java金融', passWord='12345', sign='阿里雲', url='http://aliyun.com/send'}開始傳送簡訊==》簡訊內容:用阿里雲傳送簡訊
SmsMessage{userName='java金融', passWord='6666', sign='騰訊雲', url='http://tencent.com/send'}開始傳送簡訊==》簡訊內容:用騰訊雲傳送簡訊
```
至此的話我們自定義的一個`starter`就已經完成了,這個`starter`只是一個演示的`demo`,程式碼有點粗糙,專案結構也有點問題。重點看下這個實現原理就好。趕緊動動小手去實現一個自己的`starter`吧。
### 總結
- `SpringBoot starter`的出現,讓我們專案中整合其他元件變得簡單。它把簡單給了別人,把複雜留給了自己。“犧牲小我,成就大我”的思想還是值得學習的。平時我們工作中,比如要開發一個元件、或者一個工具類,也應該儘可能的讓使用方可以做到無腦使用,不要搞的太複雜,又能讓使用者可以靈活擴充套件。
### 結束
- 由於自己才疏學淺,難免會有紕漏,假如你發現了錯誤的地方,還望留言給我指出來,我會對其加以修正。
- 如果你覺得文章還不錯,你的轉發、分享、讚賞、點贊、留言就是對我最大的鼓勵。
- 感謝您的閱讀,十分歡迎並感謝您的關注。
站在巨人的肩膀上摘蘋果:
https://www.cnblogs.com/tjudzj/p/8758391.html
https://blog.springlearn.cn/post