1. 说明
IOC在容器初始化的时候,可以根据自定义条件来判断是否需要加载组件;
2. 注解说明
@Conditional可以定义在类和方法上,value值是Condition接口Class文件数组;当定义在类上的时候,该配置类下的所有方法配置都需要满足类上的条件定义才会创建组件,当被定义在方法的时候,条件满足才会创建组件;
@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();
}
Condition接口定义
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);
}
ConditionContext: 表示的是判断条件能使用的上下文(环境);
AnnotatedTypeMetadata:如果定义在类上的时候,是org.springframework.core.type.StandardAnnotationMetadata对象,表示标记的类的所有注解信息;如果定义在方法上的时候,是org.springframework.core.type.StandardMethodMetadata对象,表示是被标记的方法的信息;
3. 自定义用法
自定义条件类CustomTestConditional
public class CustomTestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 3、获取当前环境信息
Environment environment = context.getEnvironment();
// 4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
return true;
}
}
主配置类
package com.yibai.spring.annotation.main.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Conditional;
import com.yibai.spring.annotation.bean.Person;
import com.yibai.spring.annotation.condition.CustomTestCondition;
@ComponentScan(value = "com.yibai.spring.annotation")
@Conditional(value = { CustomTestCondition.class })
public class MainConfigForCondition {
@Bean
// @Conditional(value = { ColorBeanCondition.class })
public Person person() {
return new Person();
}
}
启动类
package com.yibai.spring.annotation.main;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.yibai.spring.annotation.main.config.MainConfigForCondition;
public class MainClass {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigForCondition.class);
applicationContext.close();
}
}
4. 注解分析
4.1 当@Conditional定义在类上的时候,在IOC容器初始化在生成组件定义信息阶段,注册类文件的时候会根据条件判断是否需要加载该配置文件,调用链如下:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
// 加载Class文件的时候根据条件判断是否需要加载
register(annotatedClasses);
refresh();
}
在org.springframework.context.annotation.AnnotatedBeanDefinitionReader.registerBean(Class<?>, String, Class<? extends Annotation>...)中
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
//这里面会新建Conditional接口中matches的参数之一org.springframework.core.type.StandardAnnotationMetadata.StandardAnnotationMetadata(Class<?>, boolean)的对象
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//这里则根据条件判断是否需要加载
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
//不需要这直接返回
return;
}
//下面的代码是把条件的组件生成组件的定义信息BeanDefinition,并保存
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
最后在org.springframework.context.annotation.ConditionEvaluator.shouldSkip(AnnotatedTypeMetadata, ConfigurationPhase),会调用自定义Condition接口实现类的matches方法来判断是否符合条件;
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
//如果没有标记Conditional的,这直接返回false,表示不跳过
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>();
//通过getConditionClasses方法获取@Conditional指定的value值,即具体的自定义条件类的class文件
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
//实例化自定义Condition实现类
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;
}
//获取类上@Conditional注解的value定义的数组,并返回
@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());
}
4.2 当@Conditional结合@Bean定义在方法的时候,IOC容器在初始化刷新容器阶段,根据是否满足条件,然后选择性的加载该组件,调用链如下: