Spring Boot Starter 是在 SpringBoot 元件中被提出來的一種概念,stackoverflow 上面已經有人概括了這個 starter 是什麼東西,想看完整的回答戳 這裡。
Starter POMs 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 technology 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, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.
大概意思就是說 starter 是一種對依賴的 synthesize(合成),這是什麼意思呢?我可以舉個例子來說明。
傳統的做法
在沒有 starter 之前,假如我想要在 Spring 中使用 jpa,那我可能需要做以下操作:
- 在 Maven 中引入使用的資料庫的依賴(即 JDBC 的 jar)
- 引入 jpa 的依賴
- 在 xxx.xml 中配置一些屬性資訊
- 反複的調試直到可以正常運作
需要注意的是,這裡操作在我們 每次建立一個需要用到 jpa 的項目的時候都需要重複的做一次。也許你在第一次自己建立項目的時候是在 Google 上自己搜尋了一番,花了半天時間解決掉了各種奇怪的問題之後,jpa 終于能正常運作了。有些有經驗的人會在 OneNote 上面把這次建立項目的過程給記錄下來,包括操作的步驟以及需要用到的配置檔案的内容,在下一次再建立 jpa 項目的時候,就不需要再次去 Google 了,隻需要照着筆記來,之後再把所有的配置檔案 copy&paste 就可以了。
像上面這樣的操作也不算不行,事實上我們在沒有 starter 之前都是這麼幹的,但是這樣做有幾個問題:
- 如果過程比較繁瑣,這樣一步步操作會增加出錯的可能性
- 不停地 copy&paste 不符合 Don’t repeat yourself 精神
- 在第一次配置的時候(尤其如果開發者比較小白),需要花費掉大量的時間
使用 Spring Boot Starter 提升效率
starter 的主要目的就是為了解決上面的這些問題。
starter 的理念:starter 會把所有用到的依賴都給包含進來,避免了開發者自己去引入依賴所帶來的麻煩。需要注意的是不同的 starter 是為了解決不同的依賴,是以它們内部的實作可能會有很大的差異,例如 jpa 的 starter 和 Redis 的 starter 可能實作就不一樣,這是因為 starter 的本質在于 synthesize,這是一層在邏輯層面的抽象,也許這種理念有點類似于 Docker,因為它們都是在做一個 “包裝” 的操作,如果你知道 Docker 是為了解決什麼問題的,也許你可以用 Docker 和 starter 做一個類比。
starter 的實作:雖然不同的 starter 實作起來各有差異,但是他們基本上都會使用到兩個相同的内容:ConfigurationProperties 和 AutoConfiguration。因為 Spring Boot 堅信 “約定大于配置” 這一理念,是以我們使用 ConfigurationProperties 來儲存我們的配置,并且這些配置都可以有一個預設值,即在我們沒有主動覆寫原始配置的情況下,預設值就會生效,這在很多情況下是非常有用的。除此之外,starter 的 ConfigurationProperties 還使得所有的配置屬性被聚集到一個檔案中(一般在 resources 目錄下的 application.properties),這樣我們就告别了 Spring 項目中 XML 地獄。
starter 的整體邏輯:
上面的 starter 依賴的 jar 和我們自己手動配置的時候依賴的 jar 并沒有什麼不同,是以我們可以認為 starter 其實是把這一些繁瑣的配置操作交給了自己,而把簡單交給了使用者。除了幫助使用者去除了繁瑣的建構操作,在 “約定大于配置” 的理念下,ConfigurationProperties 還幫助使用者減少了無謂的配置操作。并且因為
application.properties
檔案的存在,即使需要自定義配置,所有的配置也隻需要在一個檔案中進行,使用起來非常友善。
了解了 starter 其實就是幫助使用者簡化了配置的操作之後,要了解 starter 和被配置了 starter 的元件之間并不是競争關系,而是輔助關系,即我們可以給一個元件建立一個 starter 來讓最終使用者在使用這個元件的時候更加的簡單友善。基于這種理念,我們可以給任意一個現有的元件建立一個 starter 來讓别人在使用這個元件的時候更加的簡單友善,事實上 Spring Boot 團隊已經幫助現有大部分的流行的元件建立好了它們的 starter,你可以在這裡檢視這些 starter 的清單。
建立自己的 Spring Boot Starter
如果你想要自己建立一個 starter,那麼基本上包含以下幾步
- 建立一個 starter 項目,關于項目的命名你可以參考這裡
- 建立一個 ConfigurationProperties 用于儲存你的配置資訊(如果你的項目不使用配置資訊則可以跳過這一步,不過這種情況非常少見)
- 建立一個 AutoConfiguration,引用定義好的配置資訊;在 AutoConfiguration 中實作所有 starter 應該完成的操作,并且把這個類加入 spring.factories 配置檔案中進行聲明
- 打包項目,之後在一個 SpringBoot 項目中引入該項目依賴,然後就可以使用該 starter 了
我們來看一個例子(例子的完整代碼位于 https://github.com/RitterHou/learn-spring-boot-starter)
首先建立一個 Maven 項目,設定
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>
<artifactId>http-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 自定義starter都應該繼承自該依賴 -->
<!-- 如果自定義starter本身需要繼承其它的依賴,可以參考 https://stackoverflow.com/a/21318359 解決 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<!-- 自定義starter依賴此jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- lombok用于自動生成get、set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
</project>
建立 proterties 類來儲存配置資訊:
@ConfigurationProperties(prefix = "http") // 自動擷取配置檔案中字首為http的屬性,把值傳入對象參數
@Setter
@Getter
public class HttpProperties {
// 如果配置檔案中配置了http.url屬性,則該預設屬性會被覆寫
private String url = "http://www.baidu.com/";
}
上面這個類就是定義了一個屬性,其預設值是
http://www.baidu.com/
,我們可以通過在
application.properties
中添加配置
http.url=https://www.zhihu.com
來覆寫參數的值。
建立業務類:
@Setter
@Getter
public class HttpClient {
private String url;
// 根據url擷取網頁資料
public String getHtml() {
try {
URL url = new URL(this.url);
URLConnection urlConnection = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "utf-8"));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "error";
}
}
這個業務類的操作非常簡單,隻包含了一個
url
屬性和一個
getHtml
方法,用于擷取一個網頁的 HTML 資料,讀者看看就懂了。
建立 AutoConfiguration
@Configuration
@EnableConfigurationProperties(HttpProperties.class)
public class HttpAutoConfiguration {
@Resource
private HttpProperties properties; // 使用配置
// 在Spring上下文中建立一個對象
@Bean
@ConditionalOnMissingBean
public HttpClient init() {
HttpClient client = new HttpClient();
String url = properties.getUrl();
client.setUrl(url);
return client;
}
}
在上面的 AutoConfiguration 中我們實作了自己要求:在 Spring 的上下文中建立了一個 HttpClient 類的 bean,并且我們把 properties 中的一個參數賦給了該 bean。 關于
@ConditionalOnMissingBean
這個注解,它的意思是在該 bean 不存在的情況下此方法才會執行,這個相當于開關的角色,更多關于開關系列的注解可以參考這裡。
最後,我們在
resources
檔案夾下建立目錄
META-INF
,在目錄中建立
spring.factories
檔案,并且在
spring.factories
中配置 AutoConfiguration:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nosuchfield.httpstarter.HttpAutoConfiguration
到此,我們的 starter 已經建立完畢了,使用 Maven 打包該項目。之後建立一個 SpringBoot 項目,在項目中添加我們之前打包的 starter 作為依賴,然後使用 SringBoot 來運作我們的 starter,代碼如下:
@Component
public class RunIt {
@Resource
private HttpClient httpClient;
public void hello() {
System.out.println(httpClient.getHtml());
}
}
正常情況下此方法的執行會列印出 url
http://www.baidu.com/
的 HTML 内容,之後我們在 application.properties 中加入配置:
http.url=https://www.zhihu.com/
再次運作程式,此時列印的結果應該是知乎首頁的 HTML 了,證明 properties 中的資料确實被覆寫了。
本文轉載自:禦坂研究所
原文連結:https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/