javascript
企业级spring-boot案例-自定义Spring Boot Starter
文章目錄
- 1. 未使用Spring Boot Starter前的情況
- 2. Spring Boot Starter簡介
- 2.1 什么是Spring Boot Starter(啟動器)
- 2.2 Starter 模塊整體結構
- 2.3 Spring 官方提供的 Starter 和 Starter 命名規范
- 3. 自定義Spring Boot Starter
- 3.1 根據 starter 命名規范創建一個 spring boot 項目
- 3.2 在 pom.xml 文件中引入依賴
- 3.3 編寫`ConfigInfo.java`、`ConfigService.java`、 `ConfigServiceImpl.java`
- 3.4 編寫ConfigProperties.java
- 3.5 編寫ConfigAutoConfiguration.java
- 3.6 編寫spring.factories
- 3.7 發布自定義 starter
- 3.8 測試自定義 starter
- 3.8.1 添加config-spring-boot-starter依賴
- 3.8.2 配置application.yml
- 3.8.3 編寫 ConfigInfoController.java
- 3.8.4 啟動測試
- 4. Spring Boot Starter原理
- 4.1 自動配置原理
- 4.1.1 自動配置類的獲取與注入
- 4.1.2 自動配置的過程
- 4.2 Spring Boot自動配置使用總結
企業級spring-boot案例系列文章上線了,涵蓋了大部分企業級的spring-boot使用場景,會不定期進行更新,企業級spring-boot案例源碼地址:https://gitee.com/JourWon/spring-boot-example,歡迎各位大佬一起學習和指正
1. 未使用Spring Boot Starter前的情況
在沒有 starter 之前,假如我想要在 Spring 中使用 jpa,那我可能需要做以下操作:
需要注意的是,上面的操作在我們每次新建一個需要用到 jpa 的項目的時候都需要重復的做一次。
但在 Spring Boot 中,一切因為 Starter 變得簡單
通過上面兩個步驟,配置自動生效,具體生效的 bean 是 JpaRepositoriesAutoConfiguration,自動配置類的名字都有一個特點,叫做 xxxAutoConfiguration。
這就是使用 Spring 框架開發項目帶來的一些的問題:
- 依賴導入問題: 每個項目都需要來單獨維護自己所依賴的jar包,在項目中使用到什么功能就需要引入什么樣的依賴。手動導入依賴容易出錯,且無法統一集中管理
- 配置繁瑣: 在引入依賴之后需要做繁雜的配置,并且這些配置是每個項目來說都是必要的,例如web.xml配置(Listener配置、Filter配置、Servlet配置)、log4j配置、數據庫連接池配置等等。這些配置重復且繁雜,在不同的項目中需要進行多次重復開發,這在很大程度上降低了我們的開發效率
而在 Spring Boot 出現之后,它為我們提供了一個強大的功能來解決上述的兩個痛點,這就是 Spring Boot 的 starters(啟動器)。
2. Spring Boot Starter簡介
2.1 什么是Spring Boot Starter(啟動器)
Spring Boot 通過將我們常用的功能抽取出來,做成的一系列啟動器,這些啟動器幫我們導入了實現各個功能所需要依賴的全部組件,我們只需要在項目中引入這些 starters,需要的依賴就會全部被導入進來,并且我們通過【約定大于配置】的方式,拋棄繁雜的配置,僅需要通過配置文件來進行少量的配置就可以使用相應的功能。
starter 的實現:雖然不同的 starter 實現起來各有差異,但是他們基本上都會使用到兩個相同的內容:ConfigurationProperties 和 AutoConfiguration。因為 Spring Boot 堅信 “約定大于配置” 這一理念,所以我們使用 ConfigurationProperties 來保存我們的配置,并且這些配置都可以有一個默認值,即在我們沒有主動覆寫原始配置的情況下,默認值就會生效,這在很多情況下是非常有用的。除此之外,starter 的 ConfigurationProperties 還使得所有的配置屬性被聚集到一個文件中(一般在 resources 目錄下的 application.properties),這樣我們就告別了 Spring 項目中眾多的 XML 配置。
2.2 Starter 模塊整體結構
starter的整體實現邏輯主要由兩個基本部分組成:
xxxAutoConfiguration:自動配置類,對某個場景下需要使用到的一些組件進行自動注入,并利用xxxProperties類來進行組件相關配置
xxxProperties:某個場景下所有可配置屬性的集成,在配置文件中配置可以進行屬性值的覆蓋,按照SpringBoot官方的定義,Starer的作用就是依賴聚合,因此直接在starter內部去進行代碼實現是不符合規定的,starter應該只起到依賴導入的作用,而具體的代碼實現應該交給其他模塊來實現,然后在starter中去引用該模塊即可,因此整體的starter的構成應該如下圖所示:
可見starter模塊依賴了兩部分,一部分是一些常用依賴,另一部分就是對自動配置模塊的依賴,而xxxAutoConfiguration與xxxProperties的具體實現,都封裝在自動配置模塊中,starter實際是通過該模塊來對外提供相應的功能。
2.3 Spring 官方提供的 Starter 和 Starter 命名規范
SpringBoot 提供了非常多的 Starter,下面列出常用的幾個:
| 1 | spring-boot-starter-web | 支持 Web 開發,包括 Tomcat 和 spring-webmvc |
| 2 | spring-boot-starter-redis | 支持 Redis 鍵值存儲數據庫,包括 spring-redis |
| 3 | spring-boot-starter-test | 支持常規的測試依賴,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模塊 |
| 4 | spring-boot-starter-aop | 支持面向切面的編程即 AOP,包括 spring-aop 和 AspectJ |
| 5 | spring-boot-starter-data-elasticsearch | 支持 ElasticSearch 搜索和分析引擎,包括 spring-data-elasticsearch |
| 6 | spring-boot-starter-jdbc | 支持JDBC數據庫 |
| 7 | spring-boot-starter-data-jpa | 支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate |
可以看到這些 Starter 的名稱都是以 spring-boot-starter 為開頭,后面跟著具體的模塊名,所有官方的 Starter 遵循相似的命名模式。
根據約定,Spring Boot官方的starter命名要定義為spring-boot-starter-*,自定義或者說第三方的要命名為thirdpartyproject-spring-boot-starter
- 官方命名空間
- 前綴:spring-boot-starter-
- 模式:spring-boot-starter-模塊名
- 舉例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc
- 自定義命名空間
- 后綴:-spring-boot-starter
- 模式:模塊-spring-boot-starter
- 舉例:mybatis-spring-boot-starter
3. 自定義Spring Boot Starter
如果你想要自己創建一個 starter,那么基本上包含以下幾步
3.1 根據 starter 命名規范創建一個 spring boot 項目
創建一個名為config-spring-boot-starter的 spring boot 項目
3.2 在 pom.xml 文件中引入依賴
<!-- 自動配置依賴 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId> </dependency><!-- 將被@ConfigurationProperties注解的類的屬性注入到元數據 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional> </dependency><!-- starter非必須依賴,使用到了可以添加 --> <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional> </dependency>3.3 編寫ConfigInfo.java、ConfigService.java、 ConfigServiceImpl.java
ConfigInfo 配置信息類,用于封裝配置信息。
@Data @Builder public class ConfigInfo implements Serializable {private static final long serialVersionUID = -2878523532668902073L;/*** ID*/private String id;/*** IP地址*/private String ip;}ConfigService 示例業務接口,這里我們定義了一個configInfo接口,用于獲取配置信息。
public interface ConfigService {/*** 獲取配置信息** @return {@link ConfigInfo}*/ConfigInfo configInfo();}ConfigServiceImpl 業務邏輯實現類,用于實現功能。
public class ConfigServiceImpl implements ConfigService {/*** ID*/private String id;/*** ip*/private String ip;/*** 構造函數** @param id ID* @param ip IP*/public ConfigServiceImpl(String id, String ip) {this.id = id;this.ip = ip;}/*** 獲取配置信息** @return {@link ConfigInfo}*/@Overridepublic ConfigInfo configInfo() {return ConfigInfo.builder().id(this.id).ip(this.ip).build();}}3.4 編寫ConfigProperties.java
創建一個配置文件讀取類 ConfigurationProperties 用于保存配置信息(如果你的項目不使用配置信息則可以跳過這一步,不過這種情況非常少見)
@ConfigurationProperties注解使開發人員可以輕松地將整個文件.properties和yml文件映射到一個對象中。編寫Properties,應使用唯一的名稱空間。不要使用Spring Boot的名稱空間(如server,management,spring等)。所以應在所有配置鍵前面加上自己的名稱空間。如我們這里使用的是com.jourwon.config作為配置名稱空間。
@Data @ConfigurationProperties(value = "com.jourwon.config") public class ConfigProperties {/*** ID標識*/private String id;/*** IP地址*/private String ip;}3.5 編寫ConfigAutoConfiguration.java
創建一個 AutoConfiguration,編寫帶有@Configuration的配置類,并添加@EnableConfigurationProperties注解,@EnableConfigurationProperties作用是為了使@ConfigurationProperties注解的類生效。
@Slf4j @Configuration @EnableConfigurationProperties(value = ConfigProperties.class) public class ConfigAutoConfiguration {@Resourceprivate ConfigProperties properties;/*** 配置ExampleService** @return {@link ConfigService}*/@Bean@ConditionalOnMissingBeanpublic ConfigService configService() {log.info("Config ConfigService Start...");ConfigService service = new ConfigServiceImpl(properties.getId(), properties.getIp());log.info("Config ConfigService End.");return service;}}3.6 編寫spring.factories
在resources/META-INF/下創建spring.factories文件,并且把上一步創建的AutoConfiguration類加入 spring.factories 配置文件中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.jourwon.spring.boot.config.ConfigAutoConfiguration3.7 發布自定義 starter
在自定義 starter 項目根目錄執行 mvn install 進行打包安裝
3.8 測試自定義 starter
3.8.1 添加config-spring-boot-starter依賴
<properties><config-spring-boot-starter.version>1.0.0</config-spring-boot-starter.version> </properties><dependencies><dependency><groupId>com.jourwon.spring.boot</groupId><artifactId>config-spring-boot-starter</artifactId><version>${config-spring-boot-starter.version}</version></dependency><!-- spring-boot-starter-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency> </dependencies>3.8.2 配置application.yml
IDE 提示
在使用官方starter的時候,我們可以發現IDE可以進行提示
在application.yml配置文件添加如下配置
# 自定義starter相關配置 com:jourwon:config:id: 23145fdb-7427-42d1-ae29-a67f5be30d02ip: 127.0.0.13.8.3 編寫 ConfigInfoController.java
@RestController public class ConfigInfoController {@Resourceprivate ConfigService configService;@GetMapping("/configInfo")public String configInfo() {return JSON.toJSONString(configService.configInfo());}}3.8.4 啟動測試
打開瀏覽器,輸入 http://127.0.0.1:8080/configInfo ,你將會看到我們配置的內容。
4. Spring Boot Starter原理
首先說說原理,我們知道使用一個公用的starter的時候,只需要將相應的依賴添加的Maven的配置文件當中即可,免去了自己需要引用很多依賴類,并且SpringBoot會自動進行類的自動配置。
在導入的starter之后,SpringBoot主要幫我們完成了兩件事情:
- 相關組件的自動導入
- 相關組件的自動配置
這兩件事情統一稱為SpringBoot的自動配置
那么 SpringBoot 是如何知道要實例化哪些類,并進行自動配置的呢? 下面簡單說一下。
首先,SpringBoot 在啟動時會去依賴的starter包中尋找 resources/META-INF/spring.factories文件,然后根據文件中配置的Jar包去掃描項目所依賴的Jar包,這類似于 Java 的 SPI 機制。
第二步,根據 spring.factories配置加載AutoConfigure類。
最后,根據 @Conditional注解的條件,進行自動配置并將Bean注入Spring Context 上下文當中。
我們也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自動配置哪些類。
4.1 自動配置原理
4.1.1 自動配置類的獲取與注入
我們從主程序入口來探索一下整個過程的原理:
//標注這個類是一個springboot的應用 @SpringBootApplication public class CommunityApplication {public static void main(String[] args) {//將springboot應用啟動SpringApplication.run(CommunityApplication.class, args);} }@SpringBootApplication注解內部結構如下圖所示:
AutoConfigurationImportSelector :重點看該類中重寫的selectImports方法,看下它返回的字符串數組是如何得來的:
我們可以去到上邊提到的spring.factories文件中去看一下,找到spring官方提供的spring-boot-autoconfigure包,在其下去找一下該文件:
可以看到這個就是SpringBoot官方為我們提供的所有自動配置類的候選列表。我們可以在其中找到一個我們比較熟悉的自動配置類去看一下它內部的實現:
可以看到這些一個個的都是JavaConfig配置類,而且都通過@Bean注解向容器中注入了一些Bean
結論:
- SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的所有自動配置類的全限定類名
- 將這些自動配置類導入容器,自動配置類就生效,幫我們進行自動配置工作;
- 整個J2EE的整體解決方案和自動配置都在spring-boot-autoconfigure的jar包中;
- 它會給容器中導入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景需要的所有組件,并配置好這些組件 ;
- 有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作;
4.1.2 自動配置的過程
自動配置類被注入到容器當中后,會幫我們進行組件的自動配置和自動注入的工作,我們以HttpEncodingAutoConfiguration(Http編碼自動配置)為例解釋這個過程:
首先我們先看下SpringBoot中配置文件與POJO類之間映射的方法,這是進行自動配置的基礎。
配置集中化管理:SpringBoot中所有可配置項都集中在一個文件中(application.yml),這個文件中的配置通過@ConfigurationProperties注解來與我們程序內部定義的POJO類來產生關聯,這些POJO類統一命名為xxxProperties,并且這些xxxProperties類中各個屬性字段都有自己的默認值,這也是SpringBoot約定大于配置理念的體現,盡可能減少用戶做選擇的次數,但同時又不失靈活性。只要我們想,配置文件中的配置隨時可以覆蓋默認值。
之后,通過配合@EnableConfigurationProperties注解,就可以自動將與配置文件綁定好的這個類注入到容器中供我們使用。
自動配置類的工作流程:
- 根據限定的條件向容器中注入組件
- 使用xxxProperties對注入的組件的相關屬性進行配置
一句話總結下自動配置類的工作過程 :
- 首先容器會根據當前不同的條件判斷,決定這個配置類是否生效!
- 一但這個配置類生效;這個配置類就會給容器中添加相應組件;
- 這些組件的屬性是從對應的properties類中獲取的,這些類里面的每一個屬性又是和配置文件綁定的;
- 所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝著,配置文件可以配置什么內容,可以參照該前綴對應的屬性類中的屬性字段
4.2 Spring Boot自動配置使用總結
-
SpringBoot啟動會加載大量的自動配置類
-
我們首先可以看我們需要的功能有沒有在SpringBoot默認寫好的自動配置類當中;
-
我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)
-
給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可;
-
- xxxxAutoConfigurartion:自動配置類;給容器中添加組件
- xxxxProperties:封裝配置文件中相關屬性;
了解完自動裝配的原理后,我們來關注一個細節問題,自動配置類必須在一定的條件下才能生效;@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置里面的所有內容才生效;
| 1 | @ConditionalOnWebApplication | 當前項目是Web項目的條件下 |
| 2 | @ConditionalOnNotWebApplication | 當前項目不是Web項目的條件下 |
| 3 | @ConditionalOnResource | 類路徑下是否有指定的資源 |
| 4 | @ConditionalOnExpression | 基于SpEL表達式作為判斷條件 |
| 5 | @ConditionalOnMissingBean | 當容器中沒有指定Bean的情況下 |
| 6 | @ConditionalOnBean | 當容器中有指定的Bean的條件下 |
| 7 | @ConditionalOnSingleCandidate | 當指定的Bean在容器中只有一個,或者在有多個Bean的情況下,用來指定首選的Bean |
| 8 | @ConditionalOnJava | 基于JVM版本作為判斷條件 |
| 9 | @ConditionalOnMissingClass | 當類路徑下沒有指定的類的條件下 |
| 10 | @ConditionalOnClass | 當類路徑下有指定的類的條件下 |
| 11 | @ConditionalOnProperty | 指定的屬性是否有指定的值 |
那么多的自動配置類,必須在一定的條件下才能生效;也就是說,我們加載了這么多的配置類,但不是所有的都生效了。
我們怎么知道哪些自動配置類生效?
我們可以通過啟用 debug=true屬性;來讓控制臺打印自動配置報告,這樣我們就可以很方便的知道哪些自動配置類生效;
#在配置文件中開啟springboot的調試類 debug=truePositive matches:(自動配置類啟用的:正匹配)
Positive matches: -----------------AopAutoConfiguration matched:- @ConditionalOnClass found required classes 'org.springframework.context.annotation.EnableAspectJAutoProxy', 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice', 'org.aspectj.weaver.AnnotatedElement' (OnClassCondition)- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)AopAutoConfiguration.CglibAutoProxyConfiguration matched:- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)AuditAutoConfiguration#auditListener matched:- @ConditionalOnMissingBean (types: org.springframework.boot.actuate.audit.listener.AbstractAuditListener; SearchStrategy: all) did not find any beans (OnBeanCondition)AuditAutoConfiguration#authenticationAuditListener matched:- @ConditionalOnClass found required class 'org.springframework.security.authentication.event.AbstractAuthenticationEvent' (OnClassCondition)- @ConditionalOnMissingBean (types: org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener; SearchStrategy: all) did not find any beans (OnBeanCondition)Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負匹配)
Negative matches: -----------------ActiveMQAutoConfiguration:Did not match:- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)AopAutoConfiguration.JdkDynamicAutoProxyConfiguration:Did not match:- @ConditionalOnProperty (spring.aop.proxy-target-class=false) did not find property 'proxy-target-class' (OnPropertyCondition)AppOpticsMetricsExportAutoConfiguration:Did not match:- @ConditionalOnClass did not find required class 'io.micrometer.appoptics.AppOpticsMeterRegistry' (OnClassCondition)ArtemisAutoConfiguration:Did not match:- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)Exclusions、Unconditional classes(排除的、沒有限定條件的自動配置類):
Exclusions: -----------org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationUnconditional classes: ----------------------org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfigurationorg.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfigurationorg.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfigurationorg.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration總結
以上是生活随笔為你收集整理的企业级spring-boot案例-自定义Spring Boot Starter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python+appium实现自动化测试
- 下一篇: gradle idea java ssm