@Conditional && Condition
Posted 恒奇恒毅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Conditional && Condition相关的知识,希望对你有一定的参考价值。
一、条件注解@Conditional
@Conditional
是Spring4.0提供的一个用于条件装配的注解,其定义了一个Condition
的数组,只有当数组所有的条件都满足的时候,组件才会被导入容器。
/**
* 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 Conditions that must @linkplain Condition#matches match
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
@Conditional
注解可以有两种使用方法:
- 类型级别,任意直接或者间接标注了
@Conponent
注解的类或者注解,比如@Configuration
或者@Profile
- 方法级别,任意标注了
@Bean
注解的方法
如果一个@Configuration
类标注了@Conditional
,那么这个类所有的@Bean
方法,@ComponentScan
和@Import
的结果都受@Conditional注解的条件约束。
特别要注意的是:@Conditional
是不支持继承的,任何父类的条件注解或者方法继承的条件注解都不会生效。为了强化这些语义,@Conditional
本身并没有标注@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
接口传递两个参数ConditionContext
和AnnotatedTypeMetadata
,在Condition实现类中可以直接使用这两个参数,获取环境、容器、类等相关信息。
1. ConditionContext
/**
* Context information for use by @link Conditions.
*
* @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
这些环境相关的信息。
2. AnnotatedTypeMetadata
参考 类和方法元信息、注解信息体系(AnnotatedTypeMetadata、AnnotationMetadata、ClassMetadata、MethodMetadata)
可以获取类及相关注解的相关信息。
三、@Conditional
如何被解析,Condition
方法何时调用?
@Conditional
和Condition
的相关逻辑是在类ConditionEvaluator#
中实现的。
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;
而该类根据构造方法的调用点,可知以下几个类会使用到。
AnnotatedBeanDefinitionReader
注解标注时候ClassPathScanningCandidateComponentProvider
注解扫描时候ConfigurationClassBeanDefinitionReader
、ConfigurationClassParser
(ConfigurationClassPostProcessor
) 解析Configuration
注解的过程中
四、典型应用
@Profile
@Profile
就是典型地基于@Conditional
的扩展,其条件逻辑封装在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中的应用
以上是关于@Conditional && Condition的主要内容,如果未能解决你的问题,请参考以下文章
[SpringBoot2]容器功能_底层注解&配置绑定_@Configuration&@Import&@Conditional&@ImportResource&
《CondLaneNet:a Top-to-down Lane Detection Framework Based on Conditional Convolution》论文笔记