spring @Conditional的使用与扩展源码分析

目录
  • @Conditional的使用
    • WindowsCondition
    • LinuxCondition
  • Conditional的扩展
    • ConditionalOnBean
    • ConditionalOnProperty
  • 源码分析

    @Conditional的使用

    @Conditional可以根据条件来判断是否注入某些Bean。

    package com.morris.spring.config;
    
    import com.morris.spring.condition.LinuxCondition;
    import com.morris.spring.condition.WindowsCondition;
    import com.morris.spring.entity.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    @Configuration
    public class ConditionalConfig {
    	// 如果是windows系统就注入bill
    	@Conditional(WindowsCondition.class)
    	@Bean(name = "user")
    	public User bill() {
    		return new User("bill", 22);
    	}
    	// 如果是linux系统就注入linus
    	@Conditional(LinuxCondition.class)
    	public User linus() {
    		return new User("linus", 20);
    }

    WindowsCondition和LinuxCondition都需要实现Condition接口。

    WindowsCondition

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    public class WindowsCondition implements Condition {
        /**
         * 根据条件判断是否注入对应的Bean
         * @param conditionContext 应用上下文
         * @param annotatedTypeMetadata 加了@Conditional注解的方法的元数据信息
         * @return
         */
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            String osName = conditionContext.getEnvironment().getProperty("os.name");
            if(osName.contains("Windows")) {
                return true;
            }
            return false;
        }
    }

    LinuxCondition

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    public class LinuxCondition implements Condition {
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            String osName = conditionContext.getEnvironment().getProperty("os.name");
            if(osName.contains("linux")) {
                return true;
            }
            return false;
        }
    }

    如果要测试LinuxCondition并不需要再linux系统下运行,只需要的启动时设置环境参数:-Dos.name=linux

    Conditional的扩展

    ConditionalOnBean

    ConditionalOnBeanc.java

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Conditional;
    import java.lang.annotation.*;
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnBeanCondition.class)
    public @interface ConditionalOnBean {
    	Class<?>[] value() default {};
    }

    OnBeanCondition.java

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    import java.util.Map;
    public class OnBeanCondition implements Condition {
    	@Override
    	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    		Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnBean.class.getName());
    		Class<?>[] clazz = (Class<?>[]) annotationAttributes.get("value");
    		for (Class<?> aClass : clazz) {
    			Map<String, ?> beans = context.getBeanFactory().getBeansOfType(aClass);
    			if(beans.isEmpty()) {
    				return false;
    			}
    		}
    		return true;
    	}
    }

    ConditionalOnProperty

    ConditionalOnProperty.java

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Conditional;
    import java.lang.annotation.*;
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {
    	String[] value() default {};
    }

    OnPropertyCondition.java

    package com.morris.spring.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    import java.util.Map;
    public class OnPropertyCondition implements Condition {
    	@Override
    	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    		Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName());
    		String[] propertyArray = (String[]) annotationAttributes.get("value");
    		for (String property : propertyArray) {
    			if(!context.getEnvironment().containsProperty(property)) {
    				return false;
    			}
    		}
    		return true;
    	}
    }

    源码分析

    如果Condition返回的是false,那么spirng就不会对方法或类进行解析。

    org.springframework.context.annotation.ConditionEvaluator#shouldSkip

    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    	// 判断类或方法上面是否有@Conditional注解
    	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();
    		// 调用condition.matches方法
    		if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    			return true;
    	return false;
    }
    

    本文转自网络,如有侵权请联系客服删除。