SpringBoot條件注解@Conditional詳細(xì)解析
一、條件注解@Conditional
@Conditional是Spring4.0提供的一個(gè)用于條件裝配的注解,其定義了一個(gè)Condition的數(shù)組,只有當(dāng)數(shù)組所有的條件都滿足的時(shí)候,組件才會(huì)被導(dǎo)入容器。
/**
* Indicates that a component is only eligible for registration when all
* {@linkplain #value specified conditions} match.
*
* <p>A <em>condition</em> is any state that can be determined programmatically
* before the bean definition is due to be registered (see {@link Condition} for details).
*
* <p>The {@code @Conditional} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes</li>
* <li>as a meta-annotation, for the purpose of composing custom stereotype
* annotations</li>
* <li>as a method-level annotation on any {@link Bean @Bean} method</li>
* </ul>
*
* <p>If a {@code @Configuration} class is marked with {@code @Conditional},
* all of the {@code @Bean} methods, {@link Import @Import} annotations, and
* {@link ComponentScan @ComponentScan} annotations associated with that
* class will be subject to the conditions.
*
* <p><strong>NOTE</strong>: Inheritance of {@code @Conditional} annotations
* is not supported; any conditions from superclasses or from overridden
* methods will not be considered. In order to enforce these semantics,
* {@code @Conditional} itself is not declared as
* {@link java.lang.annotation.Inherited @Inherited}; furthermore, any
* custom <em>composed annotation</em> that is meta-annotated with
* {@code @Conditional} must not be declared as {@code @Inherited}.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
* @see Condition
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}@Conditional注解可以有兩種使用方法:
- 類型級(jí)別,任意直接或者間接標(biāo)注了@Conponent注解的類或者注解,比如@Configuration或者@Profile
- 方法級(jí)別,任意標(biāo)注了@Bean注解的方法
如果一個(gè)@Configuration類標(biāo)注了@Conditional,那么這個(gè)類所有的@Bean方法,@ComponentScan和@Import的結(jié)果都受@Conditional注解的條件約束。 特別要注意的是:@Conditional是不支持繼承的,任何父類的條件注解或者方法繼承的條件注解都不會(huì)生效。為了強(qiáng)化這些語(yǔ)義,@Conditional本身并沒(méi)有標(biāo)注@Inherited。另外,任何使用了@Conditional注解的組合注解都不能聲明為@Inherited。
二、條件判斷接口Condition
@Conditional注解依賴于Condition接口,該接口提供真正的條件判斷邏輯。
/**
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
* <p>Conditions are checked immediately before the bean-definition is due to be
* registered and are free to veto registration based on any criteria that can
* be determined at that point.
*
* <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
* and take care to never interact with bean instances. For more fine-grained control
* of conditions that interact with {@code @Configuration} beans consider the
* {@link ConfigurationCondition} interface.
*
* @author Phillip Webb
* @since 4.0
* @see ConfigurationCondition
* @see Conditional
* @see ConditionContext
*/
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked.
* @return {@code true} if the condition matches and the component can be registered
* or {@code false} to veto registration.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}Condition接口傳遞兩個(gè)參數(shù)ConditionContext和AnnotatedTypeMetadata,在Condition實(shí)現(xiàn)類中可以直接使用這兩個(gè)參數(shù),獲取環(huán)境、容器、類等相關(guān)信息。
1. ConditionContext
/**
* Context information for use by {@link Condition}s.
*
* @author Phillip Webb
* @since 4.0
*/
public interface ConditionContext {
/**
* Return the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match or {@code null} if the registry is not available.
* @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry();
/**
* Return the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match or {@code null} if the bean factory
* is not available.
* @return the bean factory or {@code null}
*/
ConfigurableListableBeanFactory getBeanFactory();
/**
* Return the {@link Environment} for which the current application is running
* or {@code null} if no environment is available.
* @return the environment or {@code null}
*/
Environment getEnvironment();
/**
* Return the {@link ResourceLoader} currently being used or {@code null}
* if the resource loader cannot be obtained.
* @return a resource loader or {@code null}
*/
ResourceLoader getResourceLoader();
/**
* Return the {@link ClassLoader} that should be used to load additional
* classes or {@code null} if the default classloader should be used.
* @return the class loader or {@code null}
*/
ClassLoader getClassLoader();
}CondtitionContext可以獲取到BeanDefinitionRegistry、ConfigurableListableBeanFactory、Environment、ResourceLoader、ClassLoader這些環(huán)境相關(guān)的信息。
2. AnnotatedTypeMetadata
public interface AnnotatedTypeMetadata {
// 根據(jù)“全類名”判斷是否被指定 直接注解或元注解 標(biāo)注
boolean isAnnotated(String annotationName);
// 根據(jù)”全類名“獲取所有注解屬性(包括元注解)
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName);
@Nullable
// 同上,但是第二個(gè)參數(shù)傳 true 時(shí)會(huì)把屬性中對(duì)應(yīng)值為 Class 的值
// 轉(zhuǎn)為 字符串,避免需要預(yù)先加載對(duì)應(yīng) Class
Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
@Nullable
// 同上,MultiValueMap 是一個(gè) key 可以對(duì)應(yīng)多個(gè) value 的變種 map
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}
頂層接口,可被注解標(biāo)注類型(類、方法)元數(shù)據(jù)的抽象,提供了兩個(gè)核心方法:
- 根據(jù) 全類名 判斷是否被指定注解標(biāo)注
- 根據(jù) 全類名 返回指定注解的屬性集合(包括元注解)
三、@Conditional如何被解析,Condition方法何時(shí)調(diào)用?
@Conditional和Condition的相關(guān)邏輯是在類ConditionEvaluator#中實(shí)現(xiàn)的。
class ConditionEvaluator {
private final ConditionContextImpl context;
/**
* Create a new {@link ConditionEvaluator} instance.
*/
public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}
/**
* Determine if an item should be skipped based on {@code @Conditional} annotations.
* The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
* {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
* @param metadata the meta data
* @return if the item should be skipped
*/
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
return shouldSkip(metadata, null);
}
/**
* Determine if an item should be skipped based on {@code @Conditional} annotations.
* @param metadata the meta data
* @param phase the phase of the call
* @return if the item should be skipped
*/
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
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>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(this.context, metadata)) {
return true;
}
}
}
return false;
}
@SuppressWarnings("unchecked")
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
Object values = (attributes != null ? attributes.get("value") : null);
return (List<String[]>) (values != null ? values : Collections.emptyList());
}
private Condition getCondition(String conditionClassName, ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private final BeanDefinitionRegistry registry;
private final ConfigurableListableBeanFactory beanFactory;
private final Environment environment;
private final ResourceLoader resourceLoader;
public ConditionContextImpl(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = (environment != null ? environment : deduceEnvironment(registry));
this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
}
private ConfigurableListableBeanFactory deduceBeanFactory(BeanDefinitionRegistry source) {
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
if (source instanceof ConfigurableApplicationContext) {
return (((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(BeanDefinitionRegistry source) {
if (source instanceof EnvironmentCapable) {
return ((EnvironmentCapable) source).getEnvironment();
}
return null;
}
private ResourceLoader deduceResourceLoader(BeanDefinitionRegistry source) {
if (source instanceof ResourceLoader) {
return (ResourceLoader) source;
}
return null;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
@Override
public Environment getEnvironment() {
return this.environment;
}
@Override
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
@Override
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
if (this.beanFactory != null) {
return this.beanFactory.getBeanClassLoader();
}
return null;
}
}
}而該類根據(jù)構(gòu)造方法的調(diào)用點(diǎn),可知以下幾個(gè)類會(huì)使用到。

- AnnotatedBeanDefinitionReader 注解標(biāo)注時(shí)候
- ClassPathScanningCandidateComponentProvider注解掃描時(shí)候
- ConfigurationClassBeanDefinitionReader、ConfigurationClassParser(ConfigurationClassPostProcessor) 解析Configuration注解的過(guò)程中
四、典型應(yīng)用 @Profile
@Profile就是典型地基于@Conditional的擴(kuò)展,其條件邏輯封裝在ProfileCondition中
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}Springboot中的應(yīng)用

到此這篇關(guān)于SpringBoot條件注解@Conditional詳細(xì)解析的文章就介紹到這了,更多相關(guān)SpringBoot條件注解@Conditional內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java爬蟲框架之WebMagic實(shí)戰(zhàn)
這篇文章主要介紹了Java爬蟲框架之WebMagic實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java?Request獲取請(qǐng)求頭數(shù)據(jù)實(shí)例詳解
在開(kāi)發(fā)中我們經(jīng)常需要獲取用戶IP地址,通過(guò)地址來(lái)實(shí)現(xiàn)一些功能,下面這篇文章主要給大家介紹了關(guān)于Java中Request獲取請(qǐng)求頭數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2024-01-01
java實(shí)現(xiàn)動(dòng)態(tài)上傳多個(gè)文件并解決文件重名問(wèn)題
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)動(dòng)態(tài)上傳多個(gè)文件,并解決文件重名問(wèn)題的方法,感興趣的小伙伴們可以參考一下2016-03-03
Java中的InputStreamReader和OutputStreamWriter源碼分析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文通過(guò)示例代碼給大家解析了Java中的InputStreamReader和OutputStreamWriter知識(shí),需要的的朋友參考下吧2017-05-05
解決mybatis 中collection嵌套collection引發(fā)的bug
這篇文章主要介紹了解決mybatis 中collection嵌套collection引發(fā)的bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Java報(bào)錯(cuò):UnsupportedOperationException in Collection
在Java編程中,UnsupportedOperationException是一種常見(jiàn)的運(yùn)行時(shí)異常,通常在試圖對(duì)不支持的操作執(zhí)行修改時(shí)發(fā)生,它表示當(dāng)前操作不被支持,本文將深入探討UnsupportedOperationException的產(chǎn)生原因,并提供具體的解決方案和最佳實(shí)踐,需要的朋友可以參考下2024-06-06
list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作
這篇文章主要介紹了list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
實(shí)現(xiàn)quartz定時(shí)器及quartz定時(shí)器原理介紹
Quartz是一個(gè)大名鼎鼎的Java版開(kāi)源定時(shí)調(diào)度器,功能強(qiáng)悍,使用方便,下面我們看看如何使用它2013-12-12
Java 8 Lambda 表達(dá)式比較器使用示例代碼
這篇文章主要介紹了Java 8 Lambda 表達(dá)式比較器使用示例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08

