從nacos-spring-boot-project角度全面看Springboot Starter
一、作用與原理
首先說說原理,我們知道使用一個公用的starter的時候,只需要將相應的依賴新增的Maven的配置檔案當中即可,免去了自己需要引用很多依賴類,並且SpringBoot會自動進行類的自動配置。那麼 SpringBoot 是如何知道要例項化哪些類,並進行自動配置的呢? 下面簡單說一下。
首先,SpringBoot 在啟動時會去依賴的starter包中尋找 resources/META-INF/spring.factories
檔案,然後根據檔案中配置的Jar包去掃描專案所依賴的Jar包,這類似於 Java 的 SPI 機制。
第二步,根據 spring.factories
配置載入AutoConfigure
類。
最後,根據 @Conditional
註解的條件,進行自動配置並將Bean注入Spring Context 上下文當中。
我們也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class})
指定自動配置哪些類。
二、nacos-spring-boot-project starter專案解讀
官方提供的starter,大多包含兩個jar包: 一個starter——沒有任何實現,只用來管理依賴(spring.providers檔案宣告);一個autoconfigure包——包含所有具體實現,包括自動配置類,及META-INF/spring.factories檔案。自定義starter的時候,可以合併寫到一個。
官方提供的starter,命名遵循spring-boot-starter-xx; 自定義starter,命名遵循xx-spring-boot-starter。
1. nacos-spring-boot-project 包組成與命名
從專案的截圖可以看出,包的拆分上採取了類似官方的2個jar的形式;命名上採取了 自定義starter,遵循xx-spring-boot-starter的命名方式。
2. 分析nacos-config-spring-boot-starter
如上圖所示,這是一個starter——沒有任何實現,只用來管理依賴(spring.providers檔案宣告), 該宣告指明瞭依賴由
provides: nacos-config-spring-boot-autoconfigure 提供。
其pom.xml 內容如期望的一樣:僅有業務依賴和基礎starter依賴,而沒有基礎配置依賴,因為包含在了nacos-config-spring-boot-autoconfigure包中。
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>true</scope>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-spring-context</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-spring-boot-base</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
3.分析nacos-config-spring-boot-autoconfigure
如上圖所示,在resources/META-INF/
下spring.factories
檔案中如下內容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration
是整個配置的關鍵部分,其指明瞭AutoConfigure實現
類是 NacosConfigAutoConfiguration類。而該類的核心作用就是實現其註解中的相關bean在某些條件下的自動裝配。
@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
@ConditionalOnMissingBean(name = CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME)
@EnableNacosConfig
@EnableConfigurationProperties(value = NacosConfigProperties.class)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
public class NacosConfigAutoConfiguration {
}
該類通過註解進行實現,其註解的含義如下:
@ConditionalOnClass
,當classpath
下發現該類的情況下進行自動配置。@ConditionalOnMissingBean
,當Spring Context
中不存在該Bean
時進行自動配置。@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
時進行自動配置。
其中NacosConfigConstants類的如下:
public interface NacosConfigConstants {
String ENDPOINT_PREFIX = "nacos-config";
String ENABLED = EnableNacosConfig.CONFIG_PREFIX + "enabled";
String PREFIX = "nacos.config";
}
有引用到了另一個註解
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({NacosConfigBeanDefinitionRegistrar.class})
public @interface EnableNacosConfig {
String CONFIG_PREFIX = "nacos.config.";
String ENDPOINT_PLACEHOLDER = "${nacos.config.endpoint:${nacos.endpoint:}}";
String NAMESPACE_PLACEHOLDER = "${nacos.config.namespace:${nacos.namespace:}}";
String ACCESS_KEY_PLACEHOLDER = "${nacos.config.access-key:${nacos.access-key:}}";
String SECRET_KEY_PLACEHOLDER = "${nacos.config.secret-key:${nacos.secret-key:}}";
String SERVER_ADDR_PLACEHOLDER = "${nacos.config.server-addr:${nacos.server-addr:}}";
String CONTEXT_PATH_PLACEHOLDER = "${nacos.config.context-path:${nacos.context-path:}}";
String CLUSTER_NAME_PLACEHOLDER = "${nacos.config.cluster-name:${nacos.cluster-name:}}";
String ENCODE_PLACEHOLDER = "${nacos.config.encode:${nacos.encode:UTF-8}}";
NacosProperties globalProperties() default @NacosProperties(
endpoint = "${nacos.config.endpoint:${nacos.endpoint:}}",
namespace = "${nacos.config.namespace:${nacos.namespace:}}",
accessKey = "${nacos.config.access-key:${nacos.access-key:}}",
secretKey = "${nacos.config.secret-key:${nacos.secret-key:}}",
serverAddr = "${nacos.config.server-addr:${nacos.server-addr:}}",
contextPath = "${nacos.config.context-path:${nacos.context-path:}}",
clusterName = "${nacos.config.cluster-name:${nacos.cluster-name:}}",
encode = "${nacos.config.encode:${nacos.encode:UTF-8}}"
);
}
從上面的類和註解可以看出,其實,就相當於更為常見的 @ConditionalOnProperty(prefix = "nacos.config",value = "enabled",havingValue = "true")。
從上面可以看出,其毫無例外的都用都了基於字首的自動配置進行bean的裝配,那麼最終依賴如下
spring-boot-configuration-processor 也是必然的了。
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
在最上面的紅框中我們看到了 spring-configuration-metadata.json 核心作用是使用時給出自動給出IDE提示,需要料及而更多請參照:Springboot實現基於字首的自定義配置和自動提示功能。
進一步,看下pom.xml中依賴了哪些包?
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<optional>true</optional>
</dependency>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-spring-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-spring-boot-base</artifactId>
<optional>true</optional>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
最終,確實依賴到了如下2個包:
spring-boot-autoconfigure
spring-boot-configuration-processor
附加額外註解解釋:
@ConditionalOnBean:當容器中有指定的Bean的條件下
@ConditionalOnClass:當類路徑下有指定的類的條件下
@ConditionalOnExpression:基於SpEL表示式作為判斷條件
@ConditionalOnJava:基於JVM版本作為判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查詢指定的位置
@ConditionalOnMissingBean:當容器中沒有指定Bean的情況下
@ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下
@ConditionalOnNotWebApplication:當前專案不是Web專案的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑下是否有指定的資源
@ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者在有多個Bean的情況下,用來指定首選的Bean @ConditionalOnWebApplication:當前專案是Web專案的條件下
參考: 1. 編寫自己的SpringBoot-starter
相關文章
- 從原始碼角度看ContentProvider原始碼IDE
- 從頭帶你擼一個Springboot StarterSpring Boot
- 從JDK原始碼角度看LongJDK原始碼
- 從JDK原始碼角度看IntegerJDK原始碼
- 從JDK原始碼角度看FloatJDK原始碼
- 從NewSQL的角度看Apache ShardingSphereSQLApache
- 從 JDK 原始碼角度看 BooleanJDK原始碼Boolean
- SpringBoot 自定義 starterSpring Boot
- SpringBoot如何建立starterSpring Boot
- SpringBoot自定義StarterSpring Boot
- 【雜談】從實現角度看ChannelFuture
- 從語言學角度看詞嵌入模型模型
- [譯] 從設計師的角度看 ReduxRedux
- 從微服務的角度看,如何 Be Cloud Native微服務Cloud
- 從設計模式角度看OkHttp原始碼設計模式HTTP原始碼
- 從程式設計師的角度看 12306程式設計師
- 世界是平的嗎?——從不同角度看前端前端
- 從原始碼角度看CPU相關日誌原始碼
- 從 generator 的角度看 Rust 非同步程式碼Rust非同步
- [WAF攻防]從WAF攻防角度重看sql注入SQL
- 從巨集觀的角度看 Gradle 的工作過程Gradle
- 從微服務治理的角度看RSocket,. Envoy和. Istio微服務
- 從區塊鏈的角度看企業協作區塊鏈
- 從Oracle資料庫管理員的角度看PostgreSQLOracle資料庫SQL
- Springboot Starter的核心實現原理Spring Boot
- 實戰|如何自定義SpringBoot Starter?Spring Boot
- 手擼一個SpringBoot-StarterSpring Boot
- 從原始碼角度解析 Springboot 2.6.2 的啟動過程原始碼Spring Boot
- 從原始碼的角度看 Service 是如何啟動的原始碼
- 從另一個角度看拉普拉斯變換
- 從原始碼角度看traces.txt是如何生成的原始碼
- 聊聊Netty那些事兒之從核心角度看IO模型Netty模型
- 從模運算的角度看原碼和補碼
- springboot-starter中的SPI 機制Spring Boot
- SpringBoot應用篇(一):自定義starterSpring Boot
- 從JDK原始碼角度看併發競爭的超時JDK原始碼
- 從技術角度看騰訊雲“資料丟失”事件!事件
- 從作業系統角度看錶空間計算方式作業系統