Spring之Conditonal注解

12 篇文章 0 订阅
5 篇文章 0 订阅

类之Conditonal注解

扫描注解类

当解析某些注解存在的过滤器时,例如ComponentScan中 includeFilters、excludeFilters时。

return scanner.doScan(StringUtils.toStringArray(basePackages));
...
//获取候选类时判断是否需要排除
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
...
// 结果true:将候选类 添加到集合 Set<BeanDefinition> candidates = new LinkedHashSet<>()
isCandidateComponent(metadataReader)
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
		// 首先判断候选类是否存在 Conditonal 注解
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
			// 存在该注解 Conditonal 时继续判断
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

通过扫描获取到全部候选类时分别解析具体候选类

每个候选类在 checkConfigurationClassCandidate检测时返回true的前提下才会执行 Conditonal注解 注解。由此可知,Conditonal注解必须是在 Configuration、component注解等注解中使用。

// 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();
					}
					//解析是否是 Configuration 注解(Configuration、component注解均属于)
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					   // 此处解析启动类路径下具体的某个候选类:包括是否存在 Component、PropertySources、ComponentScans、ImportResource、@Bean methods等
					   //解析当前候选类上是否存在 Conditonal 注解
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 如果存在 Conditonal 注解,并且返回false,则 configurationClasses 集合中不会存在该类,也就不会添加到AOP容器中
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
...
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

涉及到全部类再执行该注解

获取spring.factories文件中配置的所有类

this.deferredImportSelectorHandler.process();

configClasses集合:spring.factories文件中配置的所有类 + 启动类路径下自定义所有的候选类。

this.reader.loadBeanDefinitions(configClasses);

示例

package net.annotate.condition;

import net.annotate.entity.Cat;
import net.annotate.entity.Custom;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration

/**
 * 类路径下面必须存在 Conditional 信息才会初始化
 */
//@ConditionalOnClass(Cat.class)
public class CustomConfig {

    @Configuration
    @Conditional(CatCondition.class)
    static class staticClass{
        static {
            System.out.println("============ 静态类");
        }
    }
}

@Slf4j
public class CatCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        System.out.println("CatCondition ...");
        return ConditionOutcome.match("return true");
    }
}

方法之Conditonal注解

以下再解析bean方法时,会执行Conditonal注解逻辑。

this.reader.loadBeanDefinitions(configClasses);
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;
		}

		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		// 解析bean方法
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

将bean方法涉及的bean注册 registerBeanDefinition。

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
		ConfigurationClass configClass = beanMethod.getConfigurationClass();
		MethodMetadata metadata = beanMethod.getMetadata();
		String methodName = metadata.getMethodName();

		// Do we need to mark the bean as skipped by its condition?
		// 获取该方法上所有与 Condition相关注解
		if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
			configClass.skippedBeanMethods.add(methodName);
			return;
		}
		if (configClass.skippedBeanMethods.contains(methodName)) {
			return;
		}

		AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
		Assert.state(bean != null, "No @Bean annotation attributes");

		// Consider name and any aliases
		List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
		String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

		// Register aliases even when overridden
		for (String alias : names) {
			this.registry.registerAlias(beanName, alias);
		}
...

		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
		beanDef.setResource(configClass.getResource());
		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

		if (metadata.isStatic()) {
			// static @Bean method
			if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
				beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
			}
			else {
				beanDef.setBeanClassName(configClass.getMetadata().getClassName());
			}
			beanDef.setUniqueFactoryMethodName(methodName);
		}
		else {
			// instance @Bean method
			beanDef.setFactoryBeanName(configClass.getBeanName());
			beanDef.setUniqueFactoryMethodName(methodName);
		}

		if (metadata instanceof StandardMethodMetadata) {
			beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
		}

		beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
				SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

		AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

		Autowire autowire = bean.getEnum("autowire");
		if (autowire.isAutowire()) {
			beanDef.setAutowireMode(autowire.value());
		}

		boolean autowireCandidate = bean.getBoolean("autowireCandidate");
		if (!autowireCandidate) {
			beanDef.setAutowireCandidate(false);
		}

		String initMethodName = bean.getString("initMethod");
		if (StringUtils.hasText(initMethodName)) {
			beanDef.setInitMethodName(initMethodName);
		}

		String destroyMethodName = bean.getString("destroyMethod");
		beanDef.setDestroyMethodName(destroyMethodName);

		// Consider scoping
		ScopedProxyMode proxyMode = ScopedProxyMode.NO;
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
		if (attributes != null) {
			beanDef.setScope(attributes.getString("value"));
			proxyMode = attributes.getEnum("proxyMode");
			if (proxyMode == ScopedProxyMode.DEFAULT) {
				proxyMode = ScopedProxyMode.NO;
			}
		}

		// Replace the original bean definition with the target one, if necessary
		BeanDefinition beanDefToRegister = beanDef;
		if (proxyMode != ScopedProxyMode.NO) {
			BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
					new BeanDefinitionHolder(beanDef, beanName), this.registry,
					proxyMode == ScopedProxyMode.TARGET_CLASS);
			beanDefToRegister = new ConfigurationClassBeanDefinition(
					(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
		}

		if (logger.isTraceEnabled()) {
			logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
					configClass.getMetadata().getClassName(), beanName));
		}
		
		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
	}
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<>();
		// getConditionClasses(metadata) 获取到全部 与condition相关的注解
		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;
	}

示例

@Configuration
/**
 * 类路径下面必须存在 Conditional 信息才会初始化
 */
//@ConditionalOnClass(Cat.class)
public class CustomConfig {

     @Bean
    @Conditional(CatCondition.class)
    @ConditionalOnMissingBean(Custom.class)
    public Cat cat(){
        System.out.println("Custom ... ");
        return new Cat();
    }
}
@Slf4j
public class CatCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return ConditionOutcome.match("return true");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于快速构建基于Spring框架的Java应用程序的开源框架。它通过提供一组注解来简化Spring应用程序的配置和开发过程。 以下是一些常用的Spring Boot注解: 1. `@SpringBootApplication`:这是一个组合注解,用于标记主应用程序类。它包含了`@Configuration`、`@EnableAutoConfiguration`和`@ComponentScan`三个注解,用于启用自动配置和组件扫描。 2. `@RestController`:用于标记一个类,表示该类是一个RESTful风格的控制器。它将类中的方法的返回值直接作为HTTP响应体返回给客户端。 3. `@RequestMapping`:用于将HTTP请求映射到控制器的处理方法上。可以指定请求的URL路径、HTTP方法等。 4. `@Autowired`:用于自动装配依赖。通过该注解Spring会自动在容器中查找匹配类型的Bean,并将其注入到标记了该注解的字段、构造方法或者方法参数中。 5. `@Value`:用于从配置文件中读取属性值。可以将属性值注入到标记了该注解的字段或者方法参数中。 6. `@Component`:用于标记一个类为Spring容器管理的组件。被标记的类会被自动扫描并注册为Bean。 7. `@Configuration`:用于标记一个类为配置类。配置类中可以定义Bean的创建和配置。 8. `@EnableAutoConfiguration`:用于启用Spring Boot的自动配置机制。Spring Boot会根据项目的依赖和配置自动配置应用程序。 9. `@Conditional`:用于根据条件来决定是否创建某个Bean。可以根据系统属性、环境变量等条件来进行判断。 10. `@EnableCaching`:用于启用Spring的缓存功能。 以上是一些常用的Spring Boot注解,它们可以帮助开发者简化配置和开发过程,提高开发效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值