Spring容器启动之org.springframework.context.annotation.ConditionEvaluator#shouldSkip方法详解

前言

在Spring容器启动之时,doRegisterBean(annotatedClass, null, null, null)的作用是向Spring容器中注入配置类bean。然而,Spring是可以进行条件注入的(通过@Conditional注解实现),这样一来就会有判断要不要注入的需求,那么如何判断一个bean要不要注入Spring容器呢?----下文的shouldSkip方法便提供了解决方法。

1.1

org.springframework.context.annotation.ConditionEvaluator#shouldSkip方法源码如下:

/**
	 * Determine if an item should be skipped based on {@code @Conditional} annotations.
	 * 基于@Conditional标签判断该item是否要跳过
     * @param metadata the meta data
	 * @param phase the phase of the call
	 * @return if the item should be skipped
	 */

//此方法前面的方法传入的参数一般是(metadata,null)
	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        //metadata为null或者配置类中不存在@Conditional标签
        //详见!metadata.isAnnotated方法的内部代码
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
			return false;
		}

//下面代码块,采用了递归方式,第一次执行的时候,phase为null,向下执行
		if (phase == null) {

//下面的判断逻辑,需要进入ConfigurationClassUtils.isConfigurationCandidate方法内部,见下1.2节
//主要的逻辑如下:
//1.metadata是AnnotationMetadata类的一个实例
//2.待注入的bean中使用了@Configuration注解
//3.待注入的bean不是一个接口
//4.待注入的bean中包含@Component @ComponentScan @Import @ImportResource中任意一个
//5.待注入的bean中含有@Bean注解
//只要满足1、2 或者 1、3 或者1、4 或者1、5则会进入下面的if判断语句中,继续递归
			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<>();
		for (String[] conditionClasses : getConditionClasses(metadata)) {
//getConditionClasses(metadata)方法可以得到@Conditional注解后面value数组
			for (String conditionClass : conditionClasses) {
//
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			}
		}
//通过Spring内部的AnnotationAwareOrder-Comparator定义的规则进行排序,有待继续探索
		AnnotationAwareOrderComparator.sort(conditions);

		for (Condition condition : conditions) {
			ConfigurationPhase requiredPhase = null;
			if (condition instanceof ConfigurationCondition) {
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			}
//requiredPhase只可能为null或者是ConfigurationCondition的一个实例对象

			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
//此逻辑为:1.requiredPhase不是ConfigurationCondition的实例
//2.phase==requiredPhase,从上述的递归可知:phase可为ConfigurationPhase.PARSE_CONFIGURATION或者ConfigurationPhase.REGISTER_BEAN
//3.condition.matches(this.context, metadata)返回false
//如果1、2或者1、3成立,则在此函数的上层将阻断bean注入Spring容器
				return true;
			}
		}

		return false;
	}

1.2

ConfigurationClassUtils.isConfigurationCandidate方法源码:

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}

对isFullConfigurationCandidate和isLiteConfigurationCandidate方法的分析分别见下1.3节与1.4接

1.3

isFullConfigurationCandidate源码如下:

	/**
	 * Check the given metadata for a full configuration class candidate
	 * (i.e. a class annotated with {@code @Configuration}).
	 * @param metadata the metadata of the annotated class
	 * @return {@code true} if the given class is to be processed as a full
	 * configuration class, including cross-method call interception
	 */
	public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
		return metadata.isAnnotated(Configuration.class.getName());
	}


//metadata.isAnnotated源码:
/**
	 * Check the given metadata for a full configuration class candidate
	 * (i.e. a class annotated with {@code @Configuration}).
	 * @param metadata the metadata of the annotated class
	 * @return {@code true} if the given class is to be processed as a full
	 * configuration class, including cross-method call interception
	 */
	public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
        //判断是否含有@Configuration注释
		return metadata.isAnnotated(Configuration.class.getName());
	}

1.4

isLiteConfigurationCandidate方法源码如下:

/**
	 * Check the given metadata for a lite configuration class candidate
	 * (e.g. a class annotated with {@code @Component} or just having
	 * {@code @Import} declarations or {@code @Bean methods}).
	 * @param metadata the metadata of the annotated class
	 * @return {@code true} if the given class is to be processed as a lite
	 * configuration class, just registering it and scanning it for {@code @Bean} methods
	 */
	public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		//待注册bean不是一个接口,返回为true
        if (metadata.isInterface()) {
			return false;
		}

		// Any of the typical annotations found?
        //candidateIndicators定义如下1.5,其实它是一个hashSet
        //逻辑为:待注册bean中含有@Component、@ComponentScan、@Import以及@ImportResource任意一个注释,返回为true
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		try {
        //待注入bean含有@Bean注释,返回为true
			return metadata.hasAnnotatedMethods(Bean.class.getName());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
			}
			return false;
		}
	}

1.5

candidateIndicators定义以及内容如下:

private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
   candidateIndicators.add(Component.class.getName());
   candidateIndicators.add(ComponentScan.class.getName());
   candidateIndicators.add(Import.class.getName());
   candidateIndicators.add(ImportResource.class.getName());
}

1.6 参考文献

【1】https://segmentfault.com/a/1190000017413393?utm_source=tag-newest

【2】https://www.bilibili.com/video/BV1tx411o77Z?p=5

【3】《Spring源码解析》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值