[springboot源码分析]-Conditional

本文详细介绍了Spring框架中的`@Conditional`注解及其使用,包括`ProfileCondition`,`Java8Condition`等自定义条件,以及`@ConditionalOnClass`在自动配置中的应用,展示了条件注解如何决定Bean是否加载。

https://www.baeldung.com/spring-conditional-annotations

Condition元数据

1 org.springframework.context.annotation.Conditional

1.1@Conditional定义

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	Class<? extends Condition>[] value();
}

2 org.springframework.context.annotation.Condition

2.1 Condition接口定义

@FunctionalInterface
public interface Condition {
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

2.2 Condition类图

在这里插入图片描述

2.2.1 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;
	}
}
2.2.2 自定义Condition
class Java8Condition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
    }
}


3.SpringBootCondition

3.1. SpringBootCondition类图

在这里插入图片描述

3.1.1 OnBeanCondition 执行图-- 粗

在这里插入图片描述

3.1.2 自定义SpringBootCondition
class Java8OrJava9 extends AnyNestedCondition {
    
    Java8OrJava9() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }
    
    @Conditional(Java8Condition.class)
    static class Java8 { }
    
    @Conditional(Java9Condition.class)
    static class Java9 { }

}

3.2 org.springframework.boot.autoconfigure.condition.ConditonOnXXX

在这里插入图片描述



4. @ConditionalOnClass(SomeService.class)执行之旅

  @AutoConfiguration
  public class MyAutoConfiguration {
 
  	@Configuration(proxyBeanMethods = false)
  	@ConditionalOnClass(SomeService.class)
  	public static class SomeServiceConfiguration {
  	}
  }

4.1 注解元数据

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
	Class<?>[] value() default {};
	String[] name() default {};
}

ConditionalOnBean.name=userService

4.2 执行逻辑线路

springApplication.run(args)
--prepareContext()
----load(context,sources)
------createBeanDefinitionLoader(context,sources):: return BeanDefinitionLoader // 1

//1
BeanDefinitionLoader.load()
--AnnotatedBeanDefinitionReader.register(source) //2

//2
AnnotatedBeanDefinitionReader.register(source)
--registerBean()
----doRegisterBean()
-----abd = new AnnotatedGenericBeanDefinition(beanClass);//3
-----ConditionEvaluator.conditionEvaluator.shouldSkip(abd.getMetadata())//4

//4.ConditionEvaluator.shouldSkip()

4.2.1 ConditionEvaluator.shouldSkip()

class ConditionEvaluator {
	public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
		return shouldSkip(metadata, null);
	}

	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
		//判断是否存在Conditional类型注解
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
			return false;
		}

		//若phase 为空,则根据metadata类型设置phase --- 略..
		List<Condition> conditions = new ArrayList<>();
		//1. 获取所有的 conditionClasses
		for (String[] conditionClasses : getConditionClasses(metadata)) { 
			for (String conditionClass : conditionClasses) {
				//2 BeanUtils.instantiateClass(conditionClass); 实例化所有的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();
			}

			//3. condition.matches:: OnClassCondition.mathes
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
				return true;
			}
		}

		return false;
	}

	//1
	private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
		Object values = (attributes != null ? attributes.get("value") : null); //OnClassCondition.class
		return (List<String[]>) (values != null ? values : Collections.emptyList());
	}
}

4.2.2 OnClassCondition.matches()



public abstract class SpringBootCondition implements Condition {

	@Override
	public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		String classOrMethodName = getClassOrMethodName(metadata);  //SomeServiceConfiguration
		ConditionOutcome outcome = getMatchOutcome(context, metadata); //子类实现;;1
		logOutcome(classOrMethodName, outcome);
		recordEvaluation(context, classOrMethodName, outcome);
		return outcome.isMatch();
	}


}


//1
class OnClassCondition extends FilteringSpringBootCondition {
	
	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		ClassLoader classLoader = context.getClassLoader();
		ConditionMessage matchMessage = ConditionMessage.empty();
		List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class); //1.1 SomeService.class

		@ConditionalOnClass处理逻辑
		if (onClasses != null) {
			List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
			//如果存在missing; 则noMatch
			if (!missing.isEmpty()) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
					.didNotFind("required class", "required classes")
					.items(Style.QUOTE, missing));
			}

			//否则 返回found
			matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
				.found("required class", "required classes")
				.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
		}

		//@ConditionalOnMissingClass 处理逻辑
		List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
		if (onMissingClasses != null) {
			List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
			if (!present.isEmpty()) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
					.found("unwanted class", "unwanted classes")
					.items(Style.QUOTE, present));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
				.didNotFind("unwanted class", "unwanted classes")
				.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
		}
		return ConditionOutcome.match(matchMessage); //返回
	}

	//1.1
	private List<String> getCandidates(AnnotatedTypeMetadata metadata, Class<?> annotationType) {
		MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
		if (attributes == null) {
			return null;
		}
		List<String> candidates = new ArrayList<>();
		addAll(candidates, attributes.get("value"));
		addAll(candidates, attributes.get("name"));
		return candidates;
	}

}
### @SpringBootApplication 注解的源码解析 @SpringBootApplication 是 Spring Boot 中一个非常重要的注解,它是一个复合注解,内部集成了多个核心功能。以下是对其源码的详细解析。 #### 1. 注解定义 @SpringBootApplication 的定义如下: ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class}) }) public @interface SpringBootApplication { // 省略部分代码 } ``` 该注解包含多个元注解和属性配置,具体作用如下[^4]。 #### 2. 核心元注解解析 - **@SpringBootConfiguration** @SpringBootConfiguration 是一个自定义的注解,其本质是 @Configuration 的增强版本。它的主要作用是标记当前类为一个配置类,用于加载 Spring 容器中的 Bean[^3]。 - **@EnableAutoConfiguration** @EnableAutoConfiguration 是 Spring Boot 自动配置的核心注解。它通过 Spring 的条件化机制(@Conditional),根据项目中引入的依赖自动完成相关组件的配置。例如,如果项目中引入了 spring-boot-starter-web 依赖,则会自动配置 Tomcat 和 Spring MVC 相关组件[^2]。 - **@ComponentScan** @ComponentScan 是 Spring 提供的一个注解,用于扫描指定包路径下的所有组件(如 @Controller、@Service、@Repository 等),并将它们注册到 Spring 容器中。在 @SpringBootApplication 中,默认扫描当前类所在包及其子包下的所有组件[^4]。 #### 3. 排除过滤器 @ComponentScan 中还定义了两个排除过滤器: - **TypeExcludeFilter**:用于排除某些特定类型的类。 - **AutoConfigurationExcludeFilter**:用于排除某些自动配置类,防止不必要的配置加载。 #### 4. 默认属性 @SpringBootApplication 提供了一些可选的属性,允许开发者自定义行为: - **scanBasePackages**:指定需要扫描的包路径,默认为当前类所在的包及其子包。 - **exclude**:排除某些自动配置类。 - **excludeName**:通过类名排除某些自动配置类。 #### 5. 示例代码 以下是一个典型的 Spring Boot 主启动类示例: ```java @SpringBootApplication(scanBasePackages = "com.example") public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 上述代码中,`scanBasePackages` 属性指定了需要扫描的包路径,确保仅加载指定包下的组件[^1]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值