深入SpringBoot:自定义Conditional

###前言 上一篇文章介绍了SpringBootEndpoint,这里再介绍下*@Conditional*。 SpringBoot的AutoConfig内部大量使用了*@Conditional*,会根据运行环境来动态注入Bean。这里介绍一些*@Conditional的使用和原理,并自定义@Conditional*来自定义功能。

Conditional

@ConditionalSpringFramework的功能,SpringBoot在它的基础上定义了*@ConditionalOnClass*,@ConditionalOnProperty的一系列的注解来实现更丰富的内容。 观察*@ConditionalOnClass会发现它注解了@Conditional(OnClassCondition.class)*。

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

OnClassCondition则继承了SpringBootCondition,实现了Condition接口。

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

查看SpringFramework的源码会发现加载使用这些注解的入口在ConfigurationClassPostProcessor中,这个实现了BeanFactoryPostProcessor接口,前面介绍过,会嵌入到Spring的加载过程。 这个类主要是从ApplicationContext中取出Configuration注解的类并解析其中的注解,包括 @Conditional@Import@Bean等。 解析 @Conditional 逻辑在ConfigurationClassParser类中,这里面用到了 ConditionEvaluator 这个类。

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
		......
	}

ConditionEvaluator中的shouldSkip方法则使用了 @Conditional中设置的Condition类。

	public boolean shouldSkip(AnnotatedTypeMetadata metadata, 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<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) {
				if (!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());
    }

自定义Conditional

所以自定义Conditional就是通过自定义注解和Condition的实现类。完整的代码在Github上了

  1. 定义*@ConditionalOnMyProperties*
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnMyPropertiesCondition.class)
    public @interface ConditionalOnMyProperties {
        String name();
    }
  1. 定义OnMyPropertiesCondition,这里继承了SpringBootCondition重用了部分功能,然后再getMatchOutcome实现了自定义的功能。
    public class OnMyPropertiesCondition extends SpringBootCondition {
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Object propertiesName = metadata.getAnnotationAttributes(ConditionalOnMyProperties.class.getName()).get("name");
            if (propertiesName != null) {
                String value = context.getEnvironment().getProperty(propertiesName.toString());
                if (value != null) {
                    return new ConditionOutcome(true, "get properties");
                }
            }
            return new ConditionOutcome(false, "none get properties");
        }
    }
  1. ConditionalOnMyProperties使用类,还要加上Configuration注解才能生效。
    @Configuration
    @ConditionalOnMyProperties(name = "message")
    public static class ConditionClass {
        @Bean
        public HelloWorld helloWorld() {
            return new HelloWorld();
        }
    }
    private static class HelloWorld {
        public void print() {
            System.out.println("hello world");
        }
    }
  1. 入口类,这里运行两次SpringApplication,传入的参数不同,第一次取Bean会抛出Bean不存在的异常,第二次就会正常输出。
    @Configuration
    @EnableAutoConfiguration
    public class CustomizeConditional {
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(CustomizeConditional.class);
            springApplication.setWebEnvironment(false);
            ConfigurableApplicationContext noneMessageConfigurableApplicationContext = springApplication.run("--logging.level.root=ERROR","--endpoints.enabled=false");
            try {
                noneMessageConfigurableApplicationContext.getBean(HelloWorld.class).print();
            } catch (Exception e) {
                e.printStackTrace();
            }
            ConfigurableApplicationContext configurableApplicationContext = springApplication.run("--message=haha", "--logging.level.root=ERROR");
            configurableApplicationContext.getBean(HelloWorld.class).print();
        }
    }

结语

ConfigurationClassPostProcessor实现了很多的扩展功能,很多额外的注解包括*@Bean*,@Import等都是由这个类解析的。

转载于:https://my.oschina.net/wcong/blog/727196

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值