Spring中的@Conditional注解實(shí)現(xiàn)分析
@Conditional注解實(shí)現(xiàn)分析
@Conditional是Spring 4出現(xiàn)的注解,但是真正露出價(jià)值的是Spring Boot的擴(kuò)展@ConditionalOnBean等。但是任然使用的是Spring框架進(jìn)行處理,并沒(méi)有做太多定制的東西,所以還是先看看@Conditional注解的實(shí)現(xiàn)。
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition Conditions} that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
先看看@Conditional注解的結(jié)構(gòu)比較簡(jiǎn)單,只需要定義一個(gè)Condition子類(lèi)即可,并且說(shuō)明只有滿(mǎn)足了當(dāng)前Condition的matches方法時(shí)才會(huì)將當(dāng)前@Component注冊(cè)成Bean。那么再看看Condition接口和體系。
/** * @since 4.0 * @see ConfigurationCondition * @see Conditional * @see ConditionContext */ @FunctionalInterface public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
當(dāng)前會(huì)傳入ConditionContext和AnnotatedTypeMetadata進(jìn)行回調(diào),返回是否匹配,如果不匹配則不會(huì)注冊(cè)成Bean。但是這是在哪里進(jìn)行回調(diào)的呢?
ConfigurationClassParser # processConfigurationClass (ConfigurationClassParser # doProcessConfigurationClass等)
ConditionEvaluator # shouldSkip
比較清楚了,又是在處理@Import、@ComponentScan、ImportSelector等的處理類(lèi)ConfigurationClassParser執(zhí)行時(shí)機(jī)比較清楚了
再看看Condition的結(jié)構(gòu)體系:
大致有四類(lèi)
1)、ProfileCondition,項(xiàng)目啟動(dòng)后Profile信息存放在ApplicationContext的Environment中,能拿到兩者之一就可以判斷。
2)、ConfigurationCondition
public interface ConfigurationCondition extends Condition { // 定義了子類(lèi)必須實(shí)現(xiàn),返回下面枚舉的一種 ConfigurationPhase getConfigurationPhase(); // 判斷階段 enum ConfigurationPhase { // Conponent階段,即將@Component加入到BeanFactory PARSE_CONFIGURATION, // 只有通過(guò)getBean才能正在將Bean注冊(cè)到Ioc容器中。前提是要將BeanDefinition添加到 // BeanFactory中 REGISTER_BEAN } }
3)、ConditionEvalutionReport,Spring Boot報(bào)表相關(guān)
4)、SpringBootCondition,直接是Spring Boot中擴(kuò)展的。下一篇博客,具體分析 @ConditionalOnBean等再具體分析。
幾個(gè)的角色比較清楚了,只要一個(gè)@Conditional注解,注解的屬性為@Condition或其子類(lèi)。根據(jù)回調(diào)@Condition的matches方法,即可判斷是否將注冊(cè)成Bean。
先看看回調(diào)時(shí)機(jī),都是在處理@Component、@ComponentSacn、ImportSelector等情況注冊(cè)Bean時(shí)。
由于情況比較復(fù)雜,可能@Component上有@ComponentScan,則會(huì)遞歸進(jìn)行處理,總之都會(huì)先調(diào)用ConfigurationClassParser的ConditionEvaluator conditionEvaluator的shouldSkip方法判斷是否跳過(guò)。
比如:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } // 省略其余代碼 }
而conditionEvaluator在ConfigurationClassParser的構(gòu)造器中被初始化,如下:
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
先看看ConditionEvaluator的類(lèi)結(jié)構(gòu)
class ConditionEvaluator { private final ConditionContextImpl context; public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry, @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { this.context = new ConditionContextImpl(registry, environment, resourceLoader); } // 省略其他方法 private static class ConditionContextImpl implements ConditionContext { private final BeanDefinitionRegistry registry; private final ConfigurableListableBeanFactory beanFactory; private final Environment environment; private final ResourceLoader resourceLoader; private final ClassLoader classLoader; public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry, @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { this.registry = registry; this.beanFactory = deduceBeanFactory(registry); this.environment = (environment != null ? environment : deduceEnvironment(registry)); this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry)); this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory); } } }
也就是說(shuō),在ConfigurationClassParser構(gòu)造器中初始化ConditionEvaluator時(shí)候,就初始化了內(nèi)部類(lèi)ConditionContextImpl,并且傳入了BeanFactory(Bean是否存在可以通過(guò)工廠(chǎng)進(jìn)行判斷)、Environment(環(huán)境配置、Profile等存放在其中)、ResourceLoader(Spring的Resource加載器)、ClassLoader(類(lèi)加載器)等。
到這里就比較清楚了,回調(diào)Condition的matches接口時(shí)傳入這些組件的類(lèi)ConditionContextImpl,要實(shí)現(xiàn)@ConditionalOnBean、@OnPropertyCondition、@Profile、@ConditionalOnClass等都比較簡(jiǎn)單了。
在調(diào)用Condition的matches時(shí),不僅傳入了ConditionContextImpl,還出入了AnnotatedTypeMetadata,這是當(dāng)前注解結(jié)合被Spring封裝的注解元信息。理解比較抽象,比如自動(dòng)裝配EmbeddedTomcat時(shí)需要同時(shí)存在Servlet、Tomcat、upgradeProtocol類(lèi);并且沒(méi)有將ServletWebServerFactory注冊(cè)成Bean,此時(shí)Component才能真正生效,如下:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { // 省略其他代碼 }
具體再看看ConditionEvaluator的shouldSkip方法的實(shí)現(xiàn):
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { // 注解信息不能為空, 并且必須有Conditional或者其子類(lèi) if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } // 如果判斷階段為空,進(jìn)行類(lèi)型判斷再遞歸調(diào)用該方法 if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); // 獲取多有的Condition類(lèi)型,如上面的EmbeddedTomcat同時(shí)需要多個(gè)條件成立 for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 條件排序(根據(jù)Spring的那三個(gè)排序方式定義) AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // 判斷階段為空(非ConfigurationCondition的子類(lèi))、不需要判斷階段,則直接返回true // 否則才調(diào)用matches接口判斷 if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; }
1)、注解信息不能為空, 并且必須有Conditional或者其子類(lèi)
2)、階段為null,則根據(jù)情況設(shè)置階段后再遞歸調(diào)用該方法
3)、獲取所有的Condition列表
4)、進(jìn)行排序
5)、遍歷Condition,是否在該階段進(jìn)行判斷。需要?jiǎng)t再調(diào)用該Condition的matches方法
總結(jié):添加了@Conditional或者@ConditionXXX注解,其value值會(huì)對(duì)應(yīng)一個(gè)Condition或者子類(lèi)的Class。在處理@ComponentScan、ImportSelector等時(shí)會(huì)根據(jù)判斷階段,調(diào)用Condition的matches方法判斷是否進(jìn)行注冊(cè)成Bean。從而實(shí)現(xiàn)各種復(fù)雜的動(dòng)態(tài)判斷注冊(cè)成Bean的情況。
到此這篇關(guān)于Spring中的@Conditional注解實(shí)現(xiàn)分析的文章就介紹到這了,更多相關(guān)@Conditional注解實(shí)現(xiàn)分析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java關(guān)于桶排序的知識(shí)點(diǎn)總結(jié)
這篇文章給大家總結(jié)了關(guān)于JAVA中J桶排序的相關(guān)知識(shí)點(diǎn)和用法分享,有興趣的讀者跟著學(xué)習(xí)下。2018-04-04SpringBoot基于MyBatis-Plus實(shí)現(xiàn)Lambda Query查詢(xún)的示例代碼
MyBatis-Plus 是 MyBatis 的增強(qiáng)工具,簡(jiǎn)化了數(shù)據(jù)庫(kù)操作,并提高了開(kāi)發(fā)效率,它提供了多種查詢(xún)方式,包括常規(guī)的 SQL 查詢(xún)、Lambda Query 查詢(xún)、分頁(yè)查詢(xún)、條件查詢(xún)等,在本篇博客中,我們將詳細(xì)講解如何使用 MyBatis-Plus 的各種查詢(xún)方式,需要的朋友可以參考下2025-01-01SpringBoot使用Hibernate攔截器實(shí)現(xiàn)時(shí)間自動(dòng)注入的操作代碼
這篇文章主要介紹了SpringBoot使用Hibernate攔截器實(shí)現(xiàn)時(shí)間自動(dòng)注入的操作代碼,主要包括hibernate攔截器的相關(guān)知識(shí),結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10java使用JDBC動(dòng)態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法
這篇文章主要介紹了java使用JDBC動(dòng)態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法,涉及JDBC操作數(shù)據(jù)庫(kù)的連接、創(chuàng)建表、添加數(shù)據(jù)、查詢(xún)等相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-08-08java基于雙向環(huán)形鏈表解決丟手帕問(wèn)題的方法示例
這篇文章主要介紹了java基于雙向環(huán)形鏈表解決丟手帕問(wèn)題的方法,簡(jiǎn)單描述了丟手帕問(wèn)題,并結(jié)合實(shí)例形式給出了Java基于雙向環(huán)形鏈表解決丟手帕問(wèn)題的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-11-11spring-AOP 及 AOP獲取request各項(xiàng)參數(shù)操作
這篇文章主要介紹了spring-AOP 及 AOP獲取request各項(xiàng)參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07