手擼一個SpringBoot-Starter

張鐵牛發表於2021-08-03

1. 簡介

通過了解SpringBoot的原理後,我們可以手擼一個spring-boot-starter來加深理解。

1.1 什麼是starter

spring官網解釋

  • 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. 開擼

spring官方學習地址

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.factoriesEnableAutoConfiguration指向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

官方地址介紹地址 github原始碼地址

名稱 描述
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

相關文章