前面我們簡(jiǎn)單介紹了如何使用消息中間件 Apache Pulsar ,但是在項(xiàng)目中那樣使用,顯然是不太好的,不管從易用性和擴(kuò)展性來(lái)看,都是遠(yuǎn)遠(yuǎn)不夠, 為了和springboot項(xiàng)目集成,寫一個(gè)pulsar-spring-boot-starter是非常有必要的,在此之前,我們先看看一個(gè)starter需要些什么。
Spring Boot Starter
spring-boot的強(qiáng)大之處在于其提供的大量starter組件,基本涵蓋了我們開發(fā)中的各個(gè)技術(shù)領(lǐng)域,比如數(shù)據(jù)庫(kù)訪問(wèn)有jdbc、jpa,緩存有redis,全文檢索有elasticsearch,消息隊(duì)列有amqp、kafka等等。
在項(xiàng)目中你只需要按需引入相應(yīng)的依賴 spring-boot-starter-xxx ,然后只需要替換對(duì)應(yīng)的配置參數(shù)即可,就能快速使用對(duì)應(yīng)的功能,不得不說(shuō)簡(jiǎn)直是為開發(fā)者插上了翅膀。
命名風(fēng)格
對(duì)于starter模塊如何命名,spring官方是這樣建議:
- Spring官方命名格式為:spring-boot-starter-{name}
- 非Spring官方建議命名格式:{name}-spring-boot-starter
準(zhǔn)備工作
如果你之前有看過(guò)spring官方starter組件,你會(huì)發(fā)現(xiàn)主要是基于AutoConfigure及@Enable來(lái)實(shí)現(xiàn)的。
- 其中AutoConfigure也就是我們常說(shuō)的自動(dòng)裝配,在spring-boot-autoconfigure包中的目錄/METE-INF/spring.factories對(duì)應(yīng)文件中,你可以看到這樣的配置:

當(dāng)啟動(dòng)Spring Boot項(xiàng)目時(shí)這些配置都會(huì)被加載(這么多的配置全部加載并處理,難怪啟動(dòng)那么慢)。
在starter中依賴的具體實(shí)現(xiàn)包中,一般都會(huì)提供一個(gè)@Enable注解作為部分?jǐn)U展功能的開關(guān),我們可以在系統(tǒng)中通過(guò)該注解引入按需引入配置

AutoConfigure配置的一定會(huì)被加載,而@Enable有開發(fā)者選擇使用使用,當(dāng)然有些組件是沒(méi)有AutoConfigure,必須通過(guò)@Enable來(lái)啟用
下面我們先對(duì)這塊內(nèi)容做個(gè)簡(jiǎn)單的認(rèn)識(shí),方便后續(xù)在寫具體starter時(shí)知道怎么寫以及為什么那樣寫。
AutoConfigure
在目錄中創(chuàng)建src/main/resources/MATE-INF中創(chuàng)建文件spring.factories,定義SpringBoot應(yīng)用啟動(dòng)時(shí)的需要注冊(cè)的配置,這個(gè)主要是基于SPI機(jī)制來(lái)實(shí)現(xiàn), 下面是當(dāng)前spring-boot-autoconfigure中spring.factories文件的部分內(nèi)容
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,
...
配置在這里的帶有@Configuration的類(如果沒(méi)有被Conditional條件過(guò)濾掉)都會(huì)作為配置將相關(guān)Bean注冊(cè)到Spring容器.
主要實(shí)現(xiàn)基于@SpringBootApplication注解上的注解@EnableAutoConfiguration
Enable
以Spring Aop相關(guān)的注解@EnableAspectJAutoProxy為例,我們看下 Spring官方是怎么使用@Enable注解來(lái)實(shí)現(xiàn)配置加載的:
@EnableAspectJAutoProxy
改注解除了一般注解的基礎(chǔ)(@Target、@Retention)元素外,還包含了兩個(gè)配置屬性proxyTargetClass、exposeProxy以及一個(gè)@Import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
@Import
在@Import中我們可以配置需要導(dǎo)入的配置類,有以下幾個(gè)選擇:
- 直接導(dǎo)入@Configuration標(biāo)識(shí)的類
- 導(dǎo)入實(shí)現(xiàn)了接口ImportBeanDefinitionRegistrar的類,來(lái)向容器注冊(cè)BeanDefinition
- 導(dǎo)入實(shí)現(xiàn)了接口ImportSelector的類(不需要@Configuration)來(lái)選擇配置
@Import(AspectJAutoProxyRegistrar.class)
ImportBeanDefinitionRegistrar
在上面@EnableAspectJAutoProxy注解上,通過(guò)@Import,引入了AspectJAutoProxyRegistrar,而該類又實(shí)現(xiàn)了接口ImportBeanDefinitionRegistrar, 該接口能夠通過(guò)BeanDefinitionRegistry向Spring容器注冊(cè)我們期望的BeanDefinition,看代碼:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
這里我們可以拿到@EnableAspectJAutoProxy的元數(shù)據(jù)以及對(duì)應(yīng)的屬性配置,這樣就可以基于開發(fā)者的配置實(shí)現(xiàn)不同邏輯
ImportSelector
上面說(shuō)到了,@Import還可以配置實(shí)現(xiàn)了ImportSelector接口的類,進(jìn)而控制具體需要使用的Configuration,下面是@EnableAsync中@Import配置的類
public class AsyncConfigurationSelector extends AdviceModeImportSelector< EnableAsync > {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
ImportAware
同樣和@Import配合使用,針對(duì)基于ImportSelector選擇的Configuration,只要實(shí)現(xiàn)了ImportAware接口,就可以拿到@Import對(duì)應(yīng)@Enable注解的元數(shù)據(jù)
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
}
上面主要根據(jù)Spring源代碼中的例子,了解@Enable、@Import、ImportBeanDefinitionRegistrar、ImportSelector、ImportAware如何搭配使用, 從而實(shí)現(xiàn)Spring的動(dòng)態(tài)配置,用一張關(guān)系圖表示:

relation
其他擴(kuò)展
spring-boot-configuration-processor
我們知道SpringBoot的配置我們都會(huì)寫在application.yml(.properties)文件中,為了簡(jiǎn)化配置工作,如果能有智能提示就好了。這不,別人也想到了。只用這樣做:
- 現(xiàn)在只需要在項(xiàng)目中引入依賴:
< dependency >
< groupId >org.springframework.boot< /groupId >
< artifactId >spring-boot-configuration-processor< /artifactId >
< optional >true< /optional >
< /dependency >
- 定義一個(gè)Properties文件
@Data
@ConfigurationProperties(prefix = "myProp")
public class MyProperties {
private Boolean enable;
private String name;
}
- 在Configuration中導(dǎo)入
@Configuration
@EnableConfigurationProperties({MyProperties.class})
public class WebApiAutoConfiguration {
}
- 打包
mvn clean install
- 生產(chǎn)metadata.json 可以看到,在jar中的/META-INF目錄下多了一個(gè)spring-configuration-metadata.json文件
@Conditional
實(shí)現(xiàn)spring bean的可插拔,我們可以基于屬性、配置、類或者Bean來(lái)控制配置(@Configuration)是否生效,常見的有下面的這些:
- ConditionalOnBean 容器存在Bean時(shí)配置有效
- ConditionalOnClass classpath中有指定class時(shí)配置有效
- ConditionalOnMissingBean 容器不存在Bean時(shí)配置有效
- ConditionalOnMissingClass classpath中沒(méi)有指定class時(shí)配置有效
- ConditionalOnProperty 屬性配置對(duì)應(yīng)值成立時(shí)配置有效
AutoConfigure和@Enable
AutoConfigure是在spring.factories中配置了就會(huì)加載,但是可以通過(guò)@Conditional讓配置中的Bean不生效;@Enable需要顯示地使用才能有效,且先于AutoConfigure生效,從而可以配合@Conditional來(lái)阻斷AutoConfigure的配置
結(jié)束語(yǔ)
關(guān)于Spring框架的學(xué)習(xí)后面會(huì)慢慢增多,我們會(huì)從原理到實(shí)踐來(lái)介紹其功能,如果你有感興趣的技術(shù)點(diǎn)或者開發(fā)中的問(wèn)題,可以通過(guò)留言進(jìn)行交流分享。
-
SPI
+關(guān)注
關(guān)注
17文章
1900瀏覽量
102193 -
緩存
+關(guān)注
關(guān)注
1文章
248瀏覽量
27826 -
數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
7文章
4085瀏覽量
68569 -
spring
+關(guān)注
關(guān)注
0文章
341瀏覽量
16060 -
Starter
+關(guān)注
關(guān)注
0文章
8瀏覽量
7848
發(fā)布評(píng)論請(qǐng)先 登錄
Spring Boot如何實(shí)現(xiàn)異步任務(wù)
Spring Boot使用Tomcat作為默認(rèn)的嵌入式服務(wù)器
Spring Boot嵌入式Web容器原理是什么
Spring Boot從零入門1 詳述
「Spring認(rèn)證」什么是Spring GraphQL?
學(xué)習(xí)Spring Boot 嵌入式服務(wù)器
Spring Boot特有的實(shí)踐
強(qiáng)大的Spring Boot 3.0要來(lái)了
Spring Boot Web相關(guān)的基礎(chǔ)知識(shí)
簡(jiǎn)述Spring Boot數(shù)據(jù)校驗(yàn)
kafka client在 spring如何實(shí)現(xiàn)
Spring Boot Actuator快速入門
Spring Boot啟動(dòng) Eureka流程
Spring Boot的啟動(dòng)原理
Spring Boot 的設(shè)計(jì)目標(biāo)
Spring Boot Starter需要些什么
評(píng)論