一、条件注解@Conditional
@Conditional 是Spring4.0提供的一个用于条件装配的注解,其定义了一个Condition 的数组,只有当数组所有的条件都满足的时候,组件才会被导入容器。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
@Conditional 注解可以有两种使用方法:
- 类型级别,任意直接或者间接标注了
@Conponent 注解的类或者注解,比如@Configuration 或者@Profile - 方法级别,任意标注了
@Bean 注解的方法
如果一个@Configuration 类标注了@Conditional ,那么这个类所有的@Bean 方法,@ComponentScan 和@Import 的结果都受@Conditional注解的条件约束。 特别要注意的是:@Conditional 是不支持继承的,任何父类的条件注解或者方法继承的条件注解都不会生效。为了强化这些语义,@Conditional 本身并没有标注@Inherited 。另外,任何使用了@Conditional 注解的组合注解都不能声明为@Inherited 。
二、条件判断接口Condition
@Conditional 注解依赖于Condition 接口,该接口提供真正的条件判断逻辑。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Condition 接口传递两个参数ConditionContext 和AnnotatedTypeMetadata ,在Condition实现类中可以直接使用这两个参数,获取环境、容器、类等相关信息。
1. ConditionContext
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
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;
public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
return shouldSkip(metadata, null);
}
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);
}
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中的应用
|