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容器在初始化刷新容器階段,根據是否滿足條件,然後選擇性的加載該組件,調用鏈如下:

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章