Spring组件注册注解之@Conditional

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容器在初始化刷新容器阶段,根据是否满足条件,然后选择性的加载该组件,调用链如下:

在org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClass, TrackedConditionEvaluator)中加载配置类的定义信息:

/**
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 */
 //这个方法就是通过配置类的定义信息解析该类本身的注解和发方法里面所有通过@Bean方式添加其他组件的所有方法
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,TrackedConditionEvaluator trackedConditionEvaluator) {
	
	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

    //判断组件是不是通过@Import导入进来的
	if (configClass.isImported()) {
		//这里加载通过主配置类的@Import方式添加的组件
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		//这里加载通过@Bean方法方式添加的组件,这里面会根据@Conditional定义的条件进行判断
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值