1. 簡介
通過了解SpringBoot的原理後,我們可以手擼一個spring-boot-starter來加深理解。
1.1 什麼是starter
- starters是一組方便的依賴描述符(根據功能特點將用到的依賴標記封裝到同一個pom中),可以將其包含在應用程式中。
- 通過starters可以獲取所需的所有Spring和相關技術的一站式服務,而無需搜尋示例程式碼或複製貼上載入的依賴項描述符。
1.2 命名規則
官方首發都遵循類似的命名模式:spring-boot-starter-*
,其中*
是特定型別的應用程式,例如: spring-boot-starter-web
。
第三方啟動器不應以spring-boot
開頭,因為它是為官方 Spring Boot 工件保留的,相反,第三方啟動器通常以專案名稱開頭,例如:ldx.spring-boot-starter
。
1.3 程式碼結構
如圖,官方的spring-boot-starter Jar中其實沒有包含程式碼,starter其實是就是一組依賴描述的集合,而其中主要包含的就是autoconfigure模組和一些必要的依賴模組。
spring-boot官方所有的auto-configuration-classes
springboot官方的starter中依賴如下:
當我們進入到官方autoconfiguration中檢視redis配置原始碼如下,而我們待會兒也會模仿RedisAutoConfiguration
寫一個自己的starter
2. 開擼
2.1 專案結構
結構說明:
├── redis-spring-boot-starter # 自定義的starter模組
│ ├── pom.xml
├── spring-boot-autoconfigure # 自定義的auto configure模組
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── ldx
│ │ └── autoconfigure
│ │ └── config
│ │ ├── RedisAutoConfiguration.java # redis 自動配置類
│ │ └── RedisProperty.java # redis property 引數繫結類
│ └── resources
│ └── META-INF
│ └── spring.factories # spring自動裝配配置檔案
└── test # 功能測試模組
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── ldx
│ │ └── test
│ │ └── TestApplication.java # 啟動類
│ └── resources
│ └── application.yaml # 資原始檔
2.2 spring-boot-autoconfigure
正如簡介中提到的一樣,該模組用於提供autoconfigure核心功能,通過META-INF/spring.factories
實現對RedisAutoConfiguration.class
的掃描,然後在RedisAutoConfiguration.class
中實現Jedis
的條件化注入,從而實現springboot的自動裝配功能。
2.2.1 匯入依賴
spring-boot-autoconfigure-processor
是一個註釋處理器,依賴用於生成META-INF/spring-autoconfigure-metadata.properties
並且被包含在專案 jar 中,其檔案記錄了當前classpath下所有的autoconfigure的元資訊,在專案啟動時會先掃描此檔案(如果存在),此檔案有助於縮短啟動時間,但不是程式正常執行所必需的。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ldx</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-autoconfigure</name>
<description>自定義的 spring-boot-autoconfigure</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--引入redis客戶端 jedis依賴-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<!-- 防止當前依賴被傳遞引用 -->
<optional>true</optional>
</dependency>
<!--自動配置註解註釋處理器,
用於生成META-INF/spring-autoconfigure-metadata.properties包含在專案中-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2.2.2 RedisProperty
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* redis 屬性注入類
*
* @author ludangxin
* @date 2021/8/1
*/
@Data
// 用於讀取配置檔案中的連結資訊
@ConfigurationProperties(prefix = "redis")
public class RedisProperty {
private String host = "localhost";
private int port = 6379;
}
2.2.3 RedisAutoConfiguration
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 redis.clients.jedis.Jedis;
/**
* redis 自動化配置類
*
* @author ludangxin
* @date 2021/8/1
*/
@ConditionalOnClass(Jedis.class)
@EnableConfigurationProperties(RedisProperty.class)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(Jedis.class)
public Jedis jedis(RedisProperty redisProperty) {
return new Jedis(redisProperty.getHost(), redisProperty.getPort());
}
}
2.2.4 spring.factories
在resources
下建立META-INF/spring.factories
,EnableAutoConfiguration
指向RedisAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
com.ldx.autoconfigure.config.RedisAutoConfiguration
2.2.5 安裝依賴
將當前模組打成Jar安裝到本地倉庫。
2.3 redis-spring-boot-starter
2.3.1 匯入依賴
在其pom中新增我們剛才建立的spring-boot-autoconfigure
模組,並且新增jedis
模組(autoconfigure模組中jedis不允許傳遞依賴因為將來autoconfigure檔案中的會有各種各樣的第三方自動化配置,不可能全部傳遞依賴,只能是用到哪個的時候就自行在starter中新增哪個即可)
<?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.ldx</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redis-spring-boot-starter</name>
<description>customize starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--引用自己建立的spring-boot-autoconfigure model-->
<dependency>
<groupId>com.ldx</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 引用jedis客戶端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
</dependencies>
</project>
2.3.2 安裝依賴
將當前模組打成Jar安裝到本地倉庫。
2.4 test
test模組為測試模組,測試starter功能。
2.4.1 匯入依賴
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ldx</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>測試springboot-starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 引用自己建立的redis-starter -->
<dependency>
<groupId>com.ldx</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.4.2 修改啟動類
package com.ldx.test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import redis.clients.jedis.Jedis;
@Slf4j
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(TestApplication.class, args);
// 獲取jedis bean
Jedis jedis = applicationContext.getBean(Jedis.class);
// add
jedis.set("name", "張三");
log.info(jedis.get("name"));
}
}
2.4.3 啟動測試
啟動專案成功獲取到了設定的資料。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.3)
2021-08-03 23:08:44.531 INFO 13930 --- [ main] com.ldx.test.TestApplication : Starting TestApplication using Java 1.8.0_261 on ludangxindeMacBook-Pro.local with PID 13930 (/Users/ludangxin/workspace/idea/redis-starter/test/target/classes started by ludangxin in /Users/ludangxin/workspace/idea/redis-starter)
2021-08-03 23:08:44.532 INFO 13930 --- [ main] com.ldx.test.TestApplication : No active profile set, falling back to default profiles: default
2021-08-03 23:08:45.062 INFO 13930 --- [ main] com.ldx.test.TestApplication : Started TestApplication in 0.901 seconds (JVM running for 1.384)
2021-08-03 23:08:45.072 INFO 13930 --- [ main] com.ldx.test.TestApplication : 張三
這時我們在test模組的application.yaml
文配置redis.port=123
測試autoconfigure模組能不能正常的獲取配置資訊。
啟動專案報錯如下:
說明配置類正確的獲取到了錯誤的配置資訊,符合預期,打完收工。
這時我們再回頭看下spring-boot-autoconfigure-processor
依賴生產的元資訊檔案如下:
3. 小結
spring-boot-starter
的出現,大大的提升了我們專案的搭建速度和質量(我們僅需匯入依賴座標,然後在配置檔案中進行簡單的配置即可。再也不用因為依賴找不全,版本對不上,依賴衝突...而煩惱了),並且官方和第三方的starter簡化了我們對中間操作(提供了通用的template,整合了對資料庫比如jpa或者amqp等操作介面),簡直不要太爽。
當我們學習瞭如何建立自己的starter後,也可以封裝我們自己的starter用於專案的建設和使用。
SpringBoot內建Starter
名稱 | 描述 |
---|---|
spring-boot-starter |
核心啟動器,包括自動配置支援、日誌記錄和 YAML |
spring-boot-starter-activemq |
使用 Apache ActiveMQ 的 JMS 訊息傳遞入門 |
spring-boot-starter-amqp |
使用 Spring AMQP 和 Rabbit MQ 的入門者 |
spring-boot-starter-aop |
使用 Spring AOP 和 AspectJ 進行面向方面程式設計的入門者 |
spring-boot-starter-artemis |
使用 Apache Artemis 進行 JMS 訊息傳遞的入門者 |
spring-boot-starter-batch |
使用 Spring Batch 的啟動器 |
spring-boot-starter-cache |
使用 Spring Framework 的快取支援的 Starter |
spring-boot-starter-data-cassandra |
Starter 使用 Cassandra 分散式資料庫和 Spring Data Cassandra |
spring-boot-starter-data-cassandra-reactive |
Starter 使用 Cassandra 分散式資料庫和 Spring Data Cassandra Reactive |
spring-boot-starter-data-couchbase |
使用 Couchbase 面向文件的資料庫和 Spring Data Couchbase 的入門者 |
spring-boot-starter-data-couchbase-reactive |
Starter 使用 Couchbase 面向文件的資料庫和 Spring Data Couchbase Reactive |
spring-boot-starter-data-elasticsearch |
使用 Elasticsearch 搜尋和分析引擎以及 Spring Data Elasticsearch 的入門者 |
spring-boot-starter-data-jdbc |
使用 Spring Data JDBC 的入門者 |
spring-boot-starter-data-jpa |
將 Spring Data JPA 與 Hibernate 結合使用的入門者 |
spring-boot-starter-data-ldap |
使用 Spring Data LDAP 的入門者 |
spring-boot-starter-data-mongodb |
使用 MongoDB 面向文件的資料庫和 Spring Data MongoDB 的入門者 |
spring-boot-starter-data-mongodb-reactive |
Starter 使用 MongoDB 面向文件的資料庫和 Spring Data MongoDB Reactive |
spring-boot-starter-data-neo4j |
使用 Neo4j 圖形資料庫和 Spring Data Neo4j 的入門者 |
spring-boot-starter-data-r2dbc |
使用 Spring Data R2DBC 的啟動器 |
spring-boot-starter-data-redis |
將 Redis 鍵值資料儲存與 Spring Data Redis 和 Lettuce 客戶端一起使用的入門者 |
spring-boot-starter-data-redis-reactive |
將 Redis 鍵值資料儲存與 Spring Data Redis 反應式和 Lettuce 客戶端一起使用的入門者 |
spring-boot-starter-data-rest |
使用 Spring Data REST 在 REST 上公開 Spring Data 儲存庫的啟動器 |
spring-boot-starter-freemarker |
使用 FreeMarker 檢視構建 MVC Web 應用程式的入門者 |
spring-boot-starter-groovy-templates |
使用 Groovy 模板檢視構建 MVC Web 應用程式的入門者 |
spring-boot-starter-hateoas |
使用 Spring MVC 和 Spring HATEOAS 構建基於超媒體的 RESTful Web 應用程式的入門者 |
spring-boot-starter-integration |
使用 Spring Integration 的入門者 |
spring-boot-starter-jdbc |
將 JDBC 與 HikariCP 連線池一起使用的 Starter |
spring-boot-starter-jersey |
使用 JAX-RS 和 Jersey 構建 RESTful Web 應用程式的初學者。替代方案spring-boot-starter-web |
spring-boot-starter-jooq |
使用 jOOQ 訪問 SQL 資料庫的入門者。spring-boot-starter-data-jpa或的替代品spring-boot-starter-jdbc |
spring-boot-starter-json |
讀寫json的Starter |
spring-boot-starter-jta-atomikos |
使用 Atomikos 的 JTA 事務入門 |
spring-boot-starter-mail |
使用 Java Mail 的 Starter 和 Spring Framework 的電子郵件傳送支援 |
spring-boot-starter-mustache |
使用 Mustache 檢視構建 Web 應用程式的入門者 |
spring-boot-starter-oauth2-client |
使用 Spring Security 的 OAuth2/OpenID Connect 客戶端功能的入門者 |
spring-boot-starter-oauth2-resource-server |
使用 Spring Security 的 OAuth2 資源伺服器功能的入門者 |
spring-boot-starter-quartz |
使用 Quartz 排程器的啟動器 |
spring-boot-starter-rsocket |
用於構建 RSocket 客戶端和伺服器的 Starter |
spring-boot-starter-security |
使用 Spring Security 的入門者 |
spring-boot-starter-test |
Starter 用於使用包括 JUnit Jupiter、Hamcrest 和 Mockito 在內的庫測試 Spring Boot 應用程式 |
spring-boot-starter-thymeleaf |
使用 Thymeleaf 檢視構建 MVC Web 應用程式的初學者 |
spring-boot-starter-validation |
將 Java Bean 驗證與 Hibernate Validator 結合使用的入門工具 |
spring-boot-starter-web |
使用 Spring MVC 構建 Web(包括 RESTful)應用程式的入門者。使用 Tomcat 作為預設的嵌入式容器 |
spring-boot-starter-web-services |
使用 Spring Web 服務的入門者 |
spring-boot-starter-webflux |
使用 Spring Framework 的 Reactive Web 支援構建 WebFlux 應用程式的 Starter |
spring-boot-starter-websocket |
使用 Spring Framework 的 WebSocket 支援構建 WebSocket 應用程式的 Starter |
除了應用程式啟動器之外,以下啟動器還可用於新增生產就緒功能:
名稱 | 描述 |
---|---|
spring-boot-starter-actuator |
使用 Spring Boot 的 Actuator 的 Starter,它提供了生產就緒的特性來幫助你監控和管理你的應用程式 |
最後,Spring Boot 還包括以下啟動器,如果您想排除或交換特定的技術方面,可以使用它們:
名稱 | 描述 |
---|---|
spring-boot-starter-jetty |
使用 Jetty 作為嵌入式 servlet 容器的啟動器。替代方案spring-boot-starter-tomcat |
spring-boot-starter-log4j2 |
使用 Log4j2 進行日誌記錄的啟動器。替代方案spring-boot-starter-logging |
spring-boot-starter-logging |
使用 Logback 進行日誌記錄的啟動器。預設日誌記錄啟動器 |
spring-boot-starter-reactor-netty |
使用 Reactor Netty 作為嵌入式響應式 HTTP 伺服器的啟動器。 |
spring-boot-starter-tomcat |
使用 Tomcat 作為嵌入式 servlet 容器的啟動器。使用的預設 servlet 容器啟動器spring-boot-starter-web |
spring-boot-starter-undertow |
使用 Undertow 作為嵌入式 servlet 容器的啟動器。替代方案spring-boot-starter-tomcat |