本文介紹了在沒有 Spring Boot 和 Starter 之前,開發人員在使用傳統的 Spring XML 開發 Web 應用時需要引用許多依賴,並且需要大量編寫 XML 代碼來描述 Bean 以及它們之間的依賴關係。也瞭解瞭如何利用 SPI 載入自定義標簽來載入 Bean 併進行註入。 ...
引言
對於 Java 開發人員來說,Spring 框架幾乎是必不可少的。它是一個廣泛用於開發企業應用程式的開源輕量級框架。近幾年,Spring Boot 在傳統 Spring 框架的基礎上應運而生,不僅提供了 Spring 的全部功能,還使開發人員更加便捷地使用。在使用 Spring Boot 時,我們經常會接觸到各種 Spring Boot Starter,例如 spring-boot-starter-web
。只需將該依賴加入項目中,我們就可以開始開發應用;在引入 spring-boot-starter-data-jdbc
後,只需在配置文件中填寫資料庫連接信息,即可連接資料庫。此外,您還可以隨意切換數據源組件依賴,而無需修改業務代碼。Spring Boot Starter 是如何適配的呢?我們能否自己實現一個 Spring Boot Starter 呢?本文將剖析 Spring Boot Starter 的原理,並自定義實現一個 Spring Boot Starter 組件。
一、Spring Boot Starter 是什麼?
Spring Boot Starter 是 Spring Boot 中比較重要的概念, 是一種依賴描述符,它可以幫助您簡化配置。當需要構建一個 Web 應用程式時,不必再遍歷所有的依賴包,一個一個地添加到項目的依賴管理中,而是只需要一個配置spring-boot-starter-web
,如以下示例:
從上面示例來看,我們使用了相當少的代碼創建了一個 REST 應用程式。Spring 官方提供了許多 Starter,同時第三方也可以自定義 Starter,官方為了加以區分,Starter 從名稱上進行瞭如下規範:spring-boot-starter-xxx
;第三方提供的 starter 名稱為:xxx-spring-boot-starter
。
二、Spring Boot Starter 剖析
前面介紹了 Starter 的概念以及如何快速創建 REST 應用程式。只需添加一個依賴和幾行代碼,就能完成 REST 介面開發。那麼,在沒有 Spring Boot 和 Starter 的情況下,我們該如何進行開發呢?Spring Boot Starter 的工作原理又是什麼?接下來,我們將通過開發 Web 服務和 Dubbo 服務作為例子,分別剖析純 Spring 和 Spring Boot Starter。
Spring
環境依賴
-
JDK 1.8
-
Maven 3
-
Tomcat 8(需要依靠 Web 容器伺服器才能啟動)
-
spring-webmvc 4.3.30.RELEASE
-
dubbo 2.7.23
開發流程
-
首先介紹一下,這是一個標準的 Maven 目錄結構與
demo-service
依賴內容<dependencies> <!-- SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.30.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- 此處需要導入databind包即可, jackson-annotations、jackson-core都不需要顯示自己的導入了--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <!-- Dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>2.7.23</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-x-discovery</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.8.0</version> </dependency> <!-- Demo API --> <dependency> <groupId>com.demo</groupId> <artifactId>demo-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
由於在 Spring XML 下還需要依靠 Java Web 和 Web 容器運行,還需要
web/WEB-INF/web.xml
Web 配置文件,內容配置了 SpringMVC 入口<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Spring監聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:dubbo.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
SpringMVC 配置文件
mvc.xml
與 Dubbo 配置文件dubbo.xml
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="com.demo.controller"/> <!-- 開啟 MVC 註解驅動 --> <mvc:annotation-driven/> <!-- 訪問靜態資源 --> <mvc:default-servlet-handler/> </beans>
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- Dubbo --> <dubbo:application name="demo-service"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20880"/> <bean id="demoServiceImpl" class="com.demo.provider.DemoServiceImpl"/> <dubbo:service interface="com.demo.api.DemoService" ref="demoServiceImpl"/> </beans>
-
編寫 Controller 介面與 Dubbo RPC 介面
package com.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping(value = "/say/hello") public HelloEntity sayHello() { return new HelloEntity("Hello World"); } }
package com.demo.provider; import com.demo.api.DemoService; import com.demo.dto.HelloEntity; public class DemoServiceImpl implements DemoService { @Override public HelloEntity sayHello() { return new HelloEntity("Hello World"); } }
-
以上還無法單獨運行,需要將以上打包成
war
包放入到 Tomcat 才可運行。
剖析
從上面的開發流程中,我們可以看到入口都在 web.xml
中。其中有一個監聽器和一個 Servlet,以及初始化參數 dubbo.xml
和 mvc.xml
。在 Spring Boot 出現之前,Spring 通常使用 XML 配置方式描述 Bean,或者在 XML 中配置註解驅動和上下文掃描方式解析 Bean。因此,我們可以看出這裡有兩個 XML 文件。經過分析源代碼,我們整理出了以下 XML 標簽解析到 Bean 解析的流程。如下:
-
由 Tomcat 啟動載入
web.xml
並通過監聽器和 Servlet 讓 Spring 載入 XML 並解析。 -
直到
BeanDefinitionParserDelegate#parseCustomElement
開始解析自定義標簽,找到mvc:xxx
或dubbo:xxx
標簽找到了 XML 命名空間。 -
DefaultNamespaceHandlerResolver
處理邏輯:以懶載入方式載入所有 jar 中META-INF/spring.handlers
(路徑必須得是這個)並緩存到handlerMappings
,通過命名空間 URI 找到與之對應的處理類,SpringMVC 與 Dubbo 命名空間處理類分別為MvcNamespaceHandler
和DubboNamespaceHandler
-
MvcNamespaceHandler
和DubboNamespaceHandler
都分別實現了NamespaceHandler#init
方法,內容如下:
init
方法將 SpringMVC 和 Dubbo 標簽對應的BeanDefinitionParser
註冊到了NamespaceHandlerSupport#parsers
中。在上一步中,DefaultNamespaceHandlerResolver
根據標簽獲取到了該標簽的BeanDefinitionParser
,從而將對應的 Bean 註冊到了 Spring IOC 容器中。註冊邏輯不是本文的重點,這裡就不再贅述。至此,SpringMVC 和 Dubbo 的載入流程已經完成。
從以上載入流程中,我們可以看出,在沒有 Spring Boot 之前,Spring 主要依靠 XML 配置來啟動。它會載入 XML 中的自定義標簽,找到對應的命名空間,然後掃描 classpath 下的 META-INF/spring.handlers
,找到命名空間處理類來解析當前標簽。
Spring Boot
環境依賴
-
JDK 1.8
-
Maven 3
-
spring-boot 2.6.9
-
dubbo 2.7.23
開發流程
-
目錄結構與 Maven
demo-spring-boot
依賴內容
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.23</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-x-discovery</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>com.demo</groupId> <artifactId>demo-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
應用程式入口
DemoSpringBootApplication
@SpringBootApplication public class DemoSpringBootApplication { public static void main(String[] args) { SpringApplication.run(DemoSpringBootApplication.class, args); } }
-
application.yml
文件內容只有 Dubbo 的配置dubbo: application: name: demo-provider protocol: port: 20880 name: dubbo registry: address: zookeeper://127.0.0.1:2181
-
編寫 Controller 介面與 Dubbo RPC 介面
package com.demo.controller; import com.demo.dto.HelloEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping(value = "/say/hello") public HelloEntity sayHello() { return new HelloEntity("Hello World"); } }
package com.demo.provider; import com.demo.api.DemoService; import com.demo.dto.HelloEntity; @DubboService public class DemoServiceImpl implements DemoService { @Override public HelloEntity sayHello() { return new HelloEntity("Hello World"); } }
-
由於
spring-boot-starter-web
已經內嵌 tomcat ,只需要直接運行DemoSpringBootApplication#main
方法即可運行應用
剖析
從開發流程上沒辦法第一時間找到解析入口,唯一入口就是在DemoSpringBootApplication
,經過源代碼分析得出以下流程:
-
應用
DemoSpringBootApplication
類上有@SpringBootApplication
註解,而該註解由以下三個註解組成:-
@SpringBootConfiguration
,標註當前類為一個配置類,與[@Configuration](https://my.oschina.net/pointdance)
註解功能一致 ,被[@Configuration](https://my.oschina.net/pointdance)
註解的類對應 Spring 的 XML 版的容器。 -
@EnableAutoConfiguration
,開啟啟動自動裝配的關鍵,由@AutoConfigurationPackage
與@Import(AutoConfigurationImportSelector.class)
組成 -
@ComponentScan
,按照當前類路徑掃描含有@Service
、@Controller
等等註解的類,等同於 Spring XML 中的context:component-scan
。
-
-
Spring Boot 自動裝配由
@EnableAutoConfiguration
導入的AutoConfigurationImportSelector
類,會調用SpringFactoriesLoader#loadFactoryNames
從 ClassPath 下掃描所有 jar 包的META-INF/spring.factories
內容,由於傳入的EnableAutoConfiguration.class
,只會返回org.springframework.boot.autoconfigure.EnableAutoConfiguration
key 的值,得到一個全限定類名字元串數組configurations
。 -
configurations
經過去重與聲明式排除後,會進行以下進行過濾自動裝配:configurations = getConfigurationClassFilter().filter(configurations)
分成兩部分:獲取過濾器和執行過濾。
-
getConfigurationClassFilter()
,也是通過SpringFactoriesLoader#loadFactoryNames
在META-INF/spring.factories
找到 Key 為org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
的值,目前只有:OnBeanCondition
、OnClassCondition
、OnClassCondition
三個過濾器。 -
執行過濾,會根據配置類上含有
@ConditionOnBean
、@ConditionalOnClass
、@ConditionalOnWebApplication
等等條件註解來過濾掉部分配置類。比如WebMvcAutoConfiguration
指定需要在@ConditionOnWebApplication
下才生效。
-
-
在引入各類 Configuration 的配置類後,配置類結合
@Bean
來完成 Spring Bean 解析和註入,同時 Spring Boot 還提供了許多@ConditionalXXX
給開發者完成靈活註入。
以上就是 Spring Boot 的自動裝配過程。Spring Boot 利用被 @Configuration
註解的配置類來代替 Spring XML 完成 Bean 的註入。然後,SpringFactoriesLoader
會最終載入 META-INF/spring.factories
中的自動配置類,實現自動裝配過程。依靠“約定大於配置”的思想,如果開發的 Starter 想要生效,就需要按照 Spring Boot 的約定。
小結
通過對比 Spring 與 Spring Boot 的開發流程,我們可以發現 Spring Boot 在完成 Web 與 Dubbo 獨立應用開發時,使用了相對較少的代碼和配置。這得益於 Spring Boot Starter 的自動裝配能力,它是 Spring Boot 的主要功能。通過消除定義一些屬於自動配置類部分的需求,自動配置可以幫助簡化開發流程並加速開發速度。
SPI
我們從上面剖析發現,兩者都使用了一項機制去載入引入的 jar 包中的配置文件從而載入對應類,那就是SPI(Service Provider Interface)
SPI (Service Provider Interface), 是 Java 內置的一種服務提供發現機制,提高框架的擴展性。
Java SPI
Java 內置的 SPI 通過java.util.ServiceLoader
類解析 Classpath 和 jar 包的META-INF/services
目錄下的以介面全限定名命名的文件,並載入該文件中指定的介面實現類,以此完成調用。
但是 Java SPI 會有一定不足:
-
不能做到按需載入,需要遍歷所有的實現並實例化,然後在迴圈中找到所需要的實現。
-
多個併發多線程使用
ServiceLoader
類的實例不安全 -
載入不到實現類時拋出並不是真正原因的異常,錯誤難定位。
Spring SPI
Spring SPI 沿用了 Java SPI ,但是在實現上和 Java SPI 存在差異,但是核心機制相同,在不修改 Spring 源碼前提下,可以做到對 Spring 框架的擴展開發。
-
在 Spring XML 中,由
DefaultNamespaceHandlerResolver
負責解析spring.handlers
生成 namespaceUri 和 NamespaceHandler 名稱的映射,等有需要時在進行實例化。 -
在 Spring Boot 中,由
SpringFactoriesLoader
負責解析spring.factories
文件,並將指定介面的所有實現類/全限定類名返回。
Spring Boot 2.7.0
在本文中 Spring Boot 自動裝配使用了 SPI 來載入到EnableAutoConfiguration
所指定的自動裝配的類名,但在 Spring Boot2.7.0之後自動裝配 SPI 機制有所改動,META-INF/spring.factories
將廢棄,同時在 Spring Boot 3 以上會將相關代碼移除,改動如下:
-
新的註解:
@AutoConfiguration
代替@Configuration
-
讀取自動裝配的類文件位置改為:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
,並且實現類全限定類名按照一行一個 -
由
org.springframework.boot.context.annotation.ImportCandidates#load
負責解析META-INF/spring/%s.imports
,其中%s
是介面名的占位符
三、Spring Boot Stater 實踐
在使用spring-boot-starter-jdbc
或者spring-boot-starter-jpa
等資料庫操作時,通常會引入一個資料庫數據源連接池,比如:HikariCP
、DBCP
等,同時可隨意切換依賴而不需要去更改任何業務代碼,開發人員也無需關註底層實現,在此我們自定義一個 Starter 同時也實現這種相容。因為我們以開發一個分散式鎖的 Starter 並擁有多個實現:Zookeeper、Redis。 在此使用 Spring Boot 2.6.9 版本。
開發
項目結構與 Maven 依賴
└── src
├── main
│ ├── java
│ │ └── com.demo.distributed.lock
│ │ ├── api
│ │ │ ├── DistributedLock.java
│ │ │ └── LockInfo.java
│ │ ├── autoconfigure
│ │ │ ├── DistributedLockAutoConfiguration.java
│ │ │ └── DistributedLockProperties.java
│ │ ├── redis
│ │ │ └── RedisDistributedLockImpl.java
│ │ └── zookeeper
│ │ └── ZookeeperDistributedLockImpl.java
│ └── resources
│ └── META-INF
│ └── spring.factories
<dependencies>
<!-- Spring Boot 自動裝配註解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 生成 META-INF/spring-configuration-metadata.json -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Zookeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
在依賴里可以看到 Zookeeper 和 Redis 依賴關係被設置為provided
,作用為編譯與測試階段使用,不會隨著項目一起發佈。即打包時不會帶上該依賴。該設置在 Spring Boot Starter 作用較大。
分散式鎖介面與實現
介面
public interface DistributedLock {
/**
* 加鎖
*/
LockInfo tryLock(String key, long expire, long waitTime);
/**
* 釋放鎖
*/
boolean release(LockInfo lock);
}
Redis 實現
public class RedisDistributedLockImpl implements DistributedLock {
private final RedissonClient client;
public RedisDistributedLockImpl(RedissonClient client) {
this.client = client;
}
@Override
public LockInfo tryLock(String key, long expire, long waitTime) {
//do something
return null;
}
@Override
public boolean release(LockInfo lock) {
//do something
return true;
}
}
Zookeeper 實現
public class ZookeeperDistributedLockImpl implements DistributedLock {
private final CuratorFramework client;
public ZookeeperDistributedLockImpl(CuratorFramework client) {
this.client = client;
}
@Override
public LockInfo tryLock(String key, long expire, long waitTime) {
return null;
}
@Override
public boolean release(LockInfo lock) {
return false;
}
}
DistributedLockAutoConfiguration 配置類
@EnableConfigurationProperties(DistributedLockProperties.class)
@Import({DistributedLockAutoConfiguration.Zookeeper.class, DistributedLockAutoConfiguration.Redis.class})
public class DistributedLockAutoConfiguration {
@Configuration
@ConditionalOnClass(CuratorFramework.class)
@ConditionalOnMissingBean(DistributedLock.class)
@ConditionalOnProperty(name = "distributed.lock.type", havingValue = "zookeeper",
matchIfMissing = true)
static class Zookeeper {
@Bean
CuratorFramework curatorFramework(DistributedLockProperties properties) {
//build CuratorFramework client
return null;
}
@Bean
ZookeeperDistributedLockImpl zookeeperDistributedLock(CuratorFramework client) {
return new ZookeeperDistributedLockImpl(client);
}
}
@Configuration
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnMissingBean(DistributedLock.class)
@ConditionalOnProperty(name = "distributed.lock.type", havingValue = "redis",
matchIfMissing = true)
static class Redis {
@Bean
RedissonClient redissonClient(DistributedLockProperties properties) {
//build RedissonClient client
return null;
}
@Bean
RedisDistributedLockImpl redisDistributedLock(RedissonClient client) {
return new RedisDistributedLockImpl(client);
}
}
}
-
@EnableConfigurationProperties(DistributedLockProperties.class)
開啟配置類 Properties 信息,會將配置文件里的信息註入 Properties 類里。 -
@Configuration
配置註解 -
@ConditionalOnClass(CuratorFramework.class)
條件註解,要求存在CuratorFramework
類當前配置類才生效,Redis 的子配置類同理。 -
@ConditionalOnMissingBean(DistributedLock.class)
條件註解,Spring 不存在DistributedLock
Bean 當前配置類才生效,Redis 的子配置類同理。 -
@ConditionalOnProperty(name = "distributed.lock.type", havingValue = "zookeeper", matchIfMissing = true)
條件註解,這裡判斷配置文件distributed.lock.type
等於zookeeper
才生效,當如果沒配置則預設當做zookeeper
,Redis 的子配置類同理。 -
@Bean
將方法返回的 Bean 註入到 Spring IOC 容器里,方法入參中含依賴的 Bean
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.distributed.lock.autoconfigure.DistributedLockAutoConfiguration
我們只需要將該文件放到resource/META-INF/spring.factories
下,就會被 Spring Boot 載入,這也是 Spring Boot 的約定大於配置的思想。
使用
Maven 依賴關係
<dependencies>
<dependency>
<groupId>com.demo</groupId>
<artifactId>distributed-lock-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<!-- Redis -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.1</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>test</id>
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
此處結合 Maven profile 功能按照不同環境依賴不同分散式鎖底層實現,同時 Spring Boot 也提供了 Spring Boot Profile 載入不同配置,可以從開發、測試、生產環境使用不同底層了,同時 Maven profile 可以根據-P
指定載入不同的依賴進行打包,解決了不同環境使用不同分散式鎖實現。
代碼使用
private final DistributedLock distributedLock;
public DemoServiceImpl(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}
public void test() {
LockInfo lock = null;
try {
lock = distributedLock.tryLock("demo", 1000, 1000);
//do something
} finally {
if (lock != null) {
distributedLock.release(lock);
}
}
}
業務代碼中由於依賴的是介面,結合 Spring Boot Starter 條件註解 + Maven Profile 不管依賴哪個分散式鎖實現,都無需去修改代碼。
四、總結
本文介紹了在沒有 Spring Boot 和 Starter 之前,開發人員在使用傳統的 Spring XML 開發 Web 應用時需要引用許多依賴,並且需要大量編寫 XML 代碼來描述 Bean 以及它們之間的依賴關係。也瞭解瞭如何利用 SPI 載入自定義標簽來載入 Bean 併進行註入。而 Spring Boot Starter 則提供了一種更加現代化的配置方式,它通過 SPI 機制載入自動裝配的 @Configuration
配置類來代替傳統的 Spring XML 完成 Bean 的註入,從而消除了大量的 XML 配置。最後,我們通過自定義開發了一個分散式鎖 Spring Boot Starter 組件,利用一系列的 @ConditionalXXX
註解和 Maven Profile 來完成開發。這樣,我們可以相容多種分散式鎖實現,並且在不同環境下使用不同的分散式鎖實現,而無需修改業務代碼。
作者:京東零售 陳炎清
來源:京東雲開發者社區