【源码解析】SpringBoot自定义条件及@Conditional源码解析

自定义注解

自定义条件类

public class MessageCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Message.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (!StringUtils.hasText(context.getEnvironment().getProperty("message")) || context.getEnvironment().getProperty("message").equals("message")) {
                    return ConditionOutcome.match(ConditionMessage.forCondition(Message.class)
                            .because("match"));
                }
            }
            return ConditionOutcome.noMatch(ConditionMessage.forCondition(Message.class)
                    .because("not match"));
        }
        return ConditionOutcome.match(ConditionMessage.forCondition(Message.class)
                .because("match"));
    }
}

自定义注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MessageCondition.class)
public @interface Message {

    String[] value();
}

当条件Message满足的时候,生成ABean对象。

@Configuration
public class MessageConfiguration {

    @Message("abc")
    public ABean aBean() {
        return new ABean();
    }
}

标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

源码解析

在spring启动过程中,会调用AbstractApplicationContext#invokeBeanFactoryPostProcessors,触发所有的BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor其中的一种,会优先执行。而最熟悉的ConfigurationClassPostProcessor是最关键的BeanDefinitionRegistryPostProcessor

类扫描

ConfigurationClassParser#doProcessConfigurationClass,挨个处理注解,对需要的bean进行扫描。

	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

ClassPathScanningCandidateComponentProvider#isConditionMatch,在类路径扫描的时候,会进行条件判断。

	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
			this.conditionEvaluator =
					new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
		}
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}

ConditionEvaluator#shouldSkip(),获取指定类上的Conditional的value,配的是Condition,加载到条件集合中,进行挨个判断。

	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable 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<>();
		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) && !condition.matches(this.context, metadata)) {
				return true;
			}
		}

		return false;
	}

	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, @Nullable ClassLoader classloader) {
		Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader);
		return (Condition) BeanUtils.instantiateClass(conditionClass);
	}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

自动装配

AutoConfigurationImportSelector#getAutoConfigurationEntry自动装配处理的时候,也会处理SpringBootCondition

  • OnBeanCondition
  • OnClassCondition
  • OnWebApplicationCondition

均继承了FilteringSpringBootCondition,该类实现了FilteringSpringBootCondition

AutoConfigurationImportSelector#getConfigurationClassFilter,加载AutoConfigurationImportFilter,使用的是Springboot的SPI。

	private ConfigurationClassFilter getConfigurationClassFilter() {
		if (this.configurationClassFilter == null) {
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			for (AutoConfigurationImportFilter filter : filters) {
				invokeAwareMethods(filter);
			}
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
		return this.configurationClassFilter;
	}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

OnPropertyCondition

OnPropertyCondition的判断条件是OnPropertyCondition.Spec#collectProperties,结果存放到noMatchmatch

@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		List<AnnotationAttributes> allAnnotationAttributes = metadata.getAnnotations()
				.stream(ConditionalOnProperty.class.getName())
				.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
				.map(MergedAnnotation::asAnnotationAttributes).collect(Collectors.toList());
		List<ConditionMessage> noMatch = new ArrayList<>();
		List<ConditionMessage> match = new ArrayList<>();
		for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
			ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
			(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
		}
		if (!noMatch.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
		}
		return ConditionOutcome.match(ConditionMessage.of(match));
	}

	private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
		Spec spec = new Spec(annotationAttributes);
		List<String> missingProperties = new ArrayList<>();
		List<String> nonMatchingProperties = new ArrayList<>();
		spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
		if (!missingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
		}
		if (!nonMatchingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.found("different value in property", "different value in properties")
					.items(Style.QUOTE, nonMatchingProperties));
		}
		return ConditionOutcome
				.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
	}

	private static class Spec {

		private final String prefix;

		private final String havingValue;

		private final String[] names;

		private final boolean matchIfMissing;

		Spec(AnnotationAttributes annotationAttributes) {
			String prefix = annotationAttributes.getString("prefix").trim();
			if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
				prefix = prefix + ".";
			}
			this.prefix = prefix;
			this.havingValue = annotationAttributes.getString("havingValue");
			this.names = getNames(annotationAttributes);
			this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
		}

		private String[] getNames(Map<String, Object> annotationAttributes) {
			String[] value = (String[]) annotationAttributes.get("value");
			String[] name = (String[]) annotationAttributes.get("name");
			Assert.state(value.length > 0 || name.length > 0,
					"The name or value attribute of @ConditionalOnProperty must be specified");
			Assert.state(value.length == 0 || name.length == 0,
					"The name and value attributes of @ConditionalOnProperty are exclusive");
			return (value.length > 0) ? value : name;
		}

		private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
			for (String name : this.names) {
				String key = this.prefix + name;
				if (resolver.containsProperty(key)) {
					if (!isMatch(resolver.getProperty(key), this.havingValue)) {
						nonMatching.add(name);
					}
				}
				else {
					if (!this.matchIfMissing) {
						missing.add(name);
					}
				}
			}
		}

		private boolean isMatch(String value, String requiredValue) {
			if (StringUtils.hasLength(requiredValue)) {
				return requiredValue.equalsIgnoreCase(value);
			}
			return !"false".equalsIgnoreCase(value);
		}

		@Override
		public String toString() {
			StringBuilder result = new StringBuilder();
			result.append("(");
			result.append(this.prefix);
			if (this.names.length == 1) {
				result.append(this.names[0]);
			}
			else {
				result.append("[");
				result.append(StringUtils.arrayToCommaDelimitedString(this.names));
				result.append("]");
			}
			if (StringUtils.hasLength(this.havingValue)) {
				result.append("=").append(this.havingValue);
			}
			result.append(")");
			return result.toString();
		}

	}

}

标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

@Profile

@Profile也是一个条件注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

	/**
	 * The set of profiles for which the annotated component should be registered.
	 */
	String[] value();

}

ProfileCondition的条件判断。

class ProfileCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			for (Object value : attrs.get("value")) {
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}

}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

@ConditionalOnExpression

注解使用OnExpressionCondition

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnExpressionCondition.class})
public @interface ConditionalOnExpression {
    String value() default "true";
}

OnExpressionCondition,封装value值,从context中获取ConfigurableListableBeanFactory,再从beanFactory获取BeanExpressionResolver,执行resolver.evaluate(expression, expressionContext);,得到结果。

@Order(Ordered.LOWEST_PRECEDENCE - 20)
class OnExpressionCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		String expression = (String) metadata.getAnnotationAttributes(ConditionalOnExpression.class.getName())
				.get("value");
		expression = wrapIfNecessary(expression);
		ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition(ConditionalOnExpression.class,
				"(" + expression + ")");
		expression = context.getEnvironment().resolvePlaceholders(expression);
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		if (beanFactory != null) {
			boolean result = evaluateExpression(beanFactory, expression);
			return new ConditionOutcome(result, messageBuilder.resultedIn(result));
		}
		return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available."));
	}

	private boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory, String expression) {
		BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
		if (resolver == null) {
			resolver = new StandardBeanExpressionResolver();
		}
		BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
		Object result = resolver.evaluate(expression, expressionContext);
		return (result != null && (boolean) result);
	}

	/**
	 * Allow user to provide bare expression with no '#{}' wrapper.
	 * @param expression source expression
	 * @return wrapped expression
	 */
	private String wrapIfNecessary(String expression) {
		if (!expression.startsWith("#{")) {
			return "#{" + expression + "}";
		}
		return expression;
	}

}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 中,@Conditional 和 @AutoConfigureAfter 是两个常用的注解,它们分别用于控制 Bean 的加载条件和 Bean 的加载顺序。下面分别对它们进行解析。 ## @Conditional @Conditional 注解用于控制 Bean 的加载条件,即只有满足指定的条件时,才会加载该 Bean。它的作用类似于 Spring Framework 中的 @Profile 注解,但更为灵活。 @Conditional 注解可以标注在配置类上或者 Bean 定义方法上,用于控制配置类或者 Bean 的加载条件。它的参数是一个实现了 Condition 接口的类,该类需要实现 matches 方法,用于指定 Bean 加载的条件。 下面是一个使用 @Conditional 注解的例子: ```java @Configuration @Conditional(MyCondition.class) public class MyConfig { @Bean public MyBean myBean() { return new MyBean(); } } ``` 这里,@Conditional(MyCondition.class) 指定了加载 MyConfig 配置类的条件,它的条件由 MyCondition 类中的 matches 方法指定。 ## @AutoConfigureAfter @AutoConfigureAfter 注解用于控制 Bean 的加载顺序,即指定在哪些 Bean 加载之后,才会加载当前 Bean。它的作用类似于 Spring Framework 中的 @DependsOn 注解,但更为灵活。 @AutoConfigureAfter 注解可以标注在配置类上或者 Bean 定义方法上,用于指定 Bean 加载的顺序。它的参数是一个字符串数组,指定了当前 Bean 依赖的其他 Bean 名称。 下面是一个使用 @AutoConfigureAfter 注解的例子: ```java @Configuration @AutoConfigureAfter({MyConfig1.class, MyConfig2.class}) public class MyConfig3 { @Bean public MyBean3 myBean3() { return new MyBean3(); } } ``` 这里,@AutoConfigureAfter({MyConfig1.class, MyConfig2.class}) 指定了在 MyConfig1 和 MyConfig2 配置类加载之后,才会加载 MyConfig3 配置类。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值