新聞中心
理解自動(dòng)配置bean
在底層,自動(dòng)配置是用標(biāo)準(zhǔn)的@Configuration類(lèi)實(shí)現(xiàn)的。附加的@Conditional注釋用于約束何時(shí)應(yīng)用自動(dòng)配置。通常,自動(dòng)配置類(lèi)使用@ConditionalOnClass和@ConditionalOnMissingBean注釋。這確保了自動(dòng)配置僅在找到相關(guān)類(lèi)且尚未聲明自己的@configuration時(shí)適用。

你可以瀏覽spring-boot-autoconfigure的源代碼,以查看Spring提供的@Configuration類(lèi)(參見(jiàn)META-INF/spring.factories 文件)。
定位候選自動(dòng)配置
Spring Boot檢查是否存在META-INF/spring.factories文件在你發(fā)布的jar中。該文件應(yīng)該在EnableAutoConfiguration為key下列出你的配置類(lèi),如下例所示:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pack.bus.autoconfigure.BusAutoConfiguration,\
com.pack.bus.autoconfigure.BusWebAutoConfiguration
自動(dòng)配置只能以這種方式加載。確保它們是在特定的包空間中定義的,并且它們永遠(yuǎn)不是組件掃描的目標(biāo)。此外,自動(dòng)配置類(lèi)不應(yīng)該允許組件掃描來(lái)查找其他組件。應(yīng)該使用特定的@Imports。
如果你的配置需要按特定順序應(yīng)用,你可以使用@AutoConfigureAfter或@AutoConfigureBefore注釋。例如,如果你提供了特定于web的配置,你的類(lèi)可能需要應(yīng)用在WebMvcAutoConfiguration之后。
可以使用@AutoConfigureOrder。該注釋具有與常規(guī)@Order注釋相同的語(yǔ)義,但為自動(dòng)配置類(lèi)提供了專(zhuān)用的順序。
與標(biāo)準(zhǔn)的@Configuration類(lèi)一樣,自動(dòng)配置類(lèi)的應(yīng)用順序只影響其bean定義的順序。隨后創(chuàng)建這些bean的順序不受影響,由每個(gè)bean的依賴(lài)關(guān)系和@DependsOn關(guān)系決定。
條件注釋
你幾乎總是希望在自動(dòng)配置類(lèi)中包含一個(gè)或多個(gè)@Conditional注解。@ConditionalOnMissingBean注解是一個(gè)常見(jiàn)的例子,它允許開(kāi)發(fā)人員在對(duì)默認(rèn)值不滿(mǎn)足時(shí)覆蓋自動(dòng)配置。
Spring Boot包含很多@Conditional注解,你可以在自己的代碼中重用這些注解,方法是注解@Configuration類(lèi)或單獨(dú)的@Bean方法。這些注釋包括:
- Class Conditions
@ConditionalOnClass和@ConditionalOnMissingClass注解讓@Configuration類(lèi)根據(jù)特定類(lèi)的存在與否被包含。由于注釋元數(shù)據(jù)是通過(guò)ASM解析的,因此你可以使用value屬性來(lái)引用真正的類(lèi),即使這個(gè)類(lèi)可能實(shí)際上沒(méi)有出現(xiàn)在正在運(yùn)行的應(yīng)用程序類(lèi)路徑中。如果想用字符串指定類(lèi)名,也可以使用name屬性。
這種機(jī)制不適用于@Bean方法,因?yàn)锧Bean方法的返回類(lèi)型通常是條件的目標(biāo):在方法的條件應(yīng)用之前,JVM將加載類(lèi)并可能處理方法引用,如果類(lèi)不存在,則這些引用將失敗。
為了處理這種情況,可以使用一個(gè)單獨(dú)的@Configuration類(lèi)來(lái)隔離這種情況,如下面的例子所示:
@Configuration(proxyBeanMethods = false)
// Some conditions ...
public class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}
如果使用@ConditionalOnClass或@ConditionalOnMissingClass作為元注釋的一部分來(lái)組合自己的組合注釋?zhuān)瑒t必須使用name,因?yàn)樵谶@種情況下引用類(lèi)不會(huì)被處理。
- Bean Conditions
@ConditionalOnBean和@ConditionalOnMissingBean注解讓一個(gè)bean根據(jù)特定bean的存在與否被包含進(jìn)來(lái)??梢允褂胿alue屬性按類(lèi)型指定bean,也可以使用name指定bean。search屬性允許您限制在搜索bean時(shí)應(yīng)該考慮的ApplicationContext層次結(jié)構(gòu)。
當(dāng)放在@Bean方法上時(shí),目標(biāo)類(lèi)型默認(rèn)為方法的返回類(lèi)型,如下面的例子所示:
@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
- Property Conditions
@ConditionalOnProperty注解讓配置基于Spring環(huán)境屬性包含。使用prefix和name屬性指定要檢查的屬性。默認(rèn)情況下,匹配任何存在且不等于false的屬性。你還可以使用havingValue和matchIfMissing屬性來(lái)創(chuàng)建更高級(jí)的檢查。
- Resource Conditions
@ConditionalOnResource注解讓配置只在特定資源存在時(shí)才包含??梢允褂贸S玫腟pring約定來(lái)指定資源,如下面的例子所示。
- Web Application Conditions
@ConditionalOnWebApplication和@ConditionalOnNotWebApplication注解讓?xiě)?yīng)用程序根據(jù)是否是“web應(yīng)用程序”來(lái)包含配置?;趕ervlet的web應(yīng)用程序是任何使用Spring WebApplicationContext、定義會(huì)話(huà)范圍或具有ConfigurableWebEnvironment的應(yīng)用程序。任何使用ReactiveWebApplicationContext或者ConfigurableReactiveWebEnvironment的應(yīng)用都可以被稱(chēng)為響應(yīng)式web應(yīng)用。
@ConditionalOnWarDeployment注解根據(jù)應(yīng)用程序是否是部署到容器中的傳統(tǒng)WAR應(yīng)用程序來(lái)包含配置。此條件不適用于與嵌入式服務(wù)器一起運(yùn)行的應(yīng)用程序。
- SpEL Expression Conditions
@ConditionalOnExpression注解讓配置基于SpEL表達(dá)式的結(jié)果包含。
創(chuàng)建自己的Starter
- 命名
你應(yīng)該確保為你的starter程序提供適當(dāng)?shù)拿臻g。即使你用了不同的Maven groupId,也不要用spring-boot來(lái)啟動(dòng)模塊名。我們可能會(huì)在未來(lái)為你的自動(dòng)配置提供官方支持。
根據(jù)經(jīng)驗(yàn),你應(yīng)該在starter之后命名一個(gè)組合模塊。例如,假設(shè)你正在為“acme”創(chuàng)建一個(gè)starter程序,并且你將自動(dòng)配置模塊命名為acme-spring-boot,而starter程序命名為acme-spring-boot-starter。如果只有一個(gè)模塊組合了這兩個(gè)模塊,請(qǐng)將其命名為acme-spring-boot-starter。
- 配置key
如果starter提供了配置key,它們使用唯一的命名空間。不要把key放在Spring Boot使用的命名空間中(比如server、management、Spring等)。
為每個(gè)屬性添加javadoc,確保配置項(xiàng)有文檔記錄,如下面的例子所示。
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* Whether to check the location of acme resources.
*/
private boolean checkLocation = true;
/**
* Timeout for establishing a connection to the acme server.
*/
private Duration loginTimeout = Duration.ofSeconds(3);
}- 完整示例
本示例主要功能是實(shí)現(xiàn)日志記錄功能
自動(dòng)配置類(lèi)
@Configuration
@EnableConfigurationProperties(LogsProperties.class)
@ConditionalOnProperty(prefix = "logs", name = "enabled", havingValue = "true")
@EnableAspectJAutoProxy
public class LogsAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LogsAutoConfiguration.class) ;
@Resource
private LogsProperties logsProperties ;
@Bean
public AspectJExpressionPointcutAdvisor logAdvisor() {
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor() ;
logger.info("執(zhí)行表達(dá)式:{}", logsProperties.getPointcut()) ;
advisor.setExpression(logsProperties.getPointcut()) ;
advisor.setAdvice(new SystemAroundOperator()) ;
return advisor ;
}
}
自定義注解
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
/**
*操作說(shuō)明
* @return
*/
String value() default "" ;
}
屬性key配置
/**
* 日志功能屬性配置
* @author xg
*/
@ConfigurationProperties(prefix = "logs")
public class LogsProperties {
/**
* 切入點(diǎn)定義
* 示例:execution(public * com.pack.controller.*.*(..))
*/
private String pointcut ;
/**
* 是否開(kāi)啟日志功能
*/
private boolean enabled = true ;
}
Advice定義
public class SystemAroundOperator implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(SystemAroundOperator.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 開(kāi)始執(zhí)行時(shí)間
long start = System.currentTimeMillis();
Method method = invocation.getMethod() ;
SystemLog annoLog = null ;
if (method.isAnnotationPresent(SystemLog.class)) {
annoLog = method.getAnnotation(SystemLog.class) ;
String value = annoLog.value() ;
try {
Object result = invocation.proceed() ;
// 方法執(zhí)行時(shí)間
Long execTime = System.currentTimeMillis() - start ;
logger.info("{}, 業(yè)務(wù)執(zhí)行時(shí)間:{} ms", value, execTime) ;
return result ;
} catch (Throwable t) {
Long execTime = System.currentTimeMillis() - start ;
logger.info("{}, 業(yè)務(wù)執(zhí)行時(shí)間:{} ms,發(fā)生異常信息:{}", value, execTime, t.getMessage()) ;
throw t ;
}
}
return invocation.proceed();
}
}配置META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pack.config.LogsAutoConfiguration
以上就實(shí)現(xiàn)自定義starter的流程。
網(wǎng)頁(yè)標(biāo)題:SpringBoot自定義自動(dòng)配置這些知識(shí)點(diǎn),你需要了解
URL分享:http://www.dlmjj.cn/article/dpgeshi.html


咨詢(xún)
建站咨詢(xún)
