12.spring系列- @Conditional注解

1.@Conditional是做什么的?
2.@Conditional多个条件是什么逻辑关系?
3.条件判断在什么时候执行?
4.ConfigurationCondition和Condition有什么区别?什么时候使用ConfigurationCondition?
5.多个Condition执行的顺序是什么样的?可以配置优先级么?
6.介绍一下@Conditional常见的一些用法么?

Conditional接口

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

	/**
	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	 //Condition类型的数组,Condition是一个接口,表示一个条件判断,内部有个方法返回true或false,当所有Condition都成立的时候,@Conditional的结果才成立。
	Class<? extends Condition>[] value();

}

Condition接口

@FunctionalInterface
public interface Condition {
	
	//context : 条件上下文
	// metadata:用来获取被@Conditional标注的对象上的所有注解信息
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

ConditionContext 接口

public interface ConditionContext {

    /**
     * 返回bean定义注册器,可以通过注册器获取bean定义的各种配置信息
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * 返回ConfigurableListableBeanFactory类型的bean工厂,相当于一个ioc容器对象
     */
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 返回当前spring容器的环境配置信息对象
     */
    Environment getEnvironment();

    /**
     * 返回资源加载器
     */
    ResourceLoader getResourceLoader();

    /**
     * 返回类加载器
     */
    @Nullable
    ClassLoader getClassLoader();

}

Conditional是一个条件判断,那什么时候执行?

Spring对配置类的处理主要分为2个阶段:

  1. 配置类解析阶段:会得到一批配置类的信息,和一些需要注册的bean
  2. bean注册阶段:将配置类解析阶段得到的配置类和需要注册的bean注册到spring容器中

什么是配置类 (ConfigurationClassUtils#isConfigurationCandidate)

  1. 类上有@Compontent注解
  2. 类上有@Configuration注解
  3. 类上有@CompontentScan注解
  4. 类上有@Import注解
  5. 类上有@ImportResource注解
  6. 类中有@Bean标注的方法

如果将Condition接口的实现类作为配置类上@Conditional中,那么这个条件会对两个阶段都有效,此时通过Condition是无法精细的控制某个阶段的,如果想控制某个阶段,比如可以让他解析,但是不能让他注册,此时就就需要用到另外一个接口了:ConfigurationCondition

ConfigurationCondition接口

public interface ConfigurationCondition extends Condition {

    /**
     * 条件判断的阶段,是在解析配置类的时候过滤还是在创建bean的时候过滤
     */
    ConfigurationPhase getConfigurationPhase();


    /**
     * 表示阶段的枚举:2个值
     */
    enum ConfigurationPhase {

        /**
         * 配置类解析阶段,如果条件为false,配置类将不会被解析
         */
        PARSE_CONFIGURATION,

        /**
         * bean注册阶段,如果为false,bean将不会被注册
         */
        REGISTER_BEAN
    }

}

案例 :阻止配置类的处理

//Condition 实现类
public class MyCondition1 implements Condition {

    //为false的时候,spring不处理这个配置类
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}

配置类:

@Conditional(MyCondition1.class)
@Configuration
public class ConditionConfig {

    @Bean
    public String name() {
        return "ss";
    }
}

测试方法:

@Test
    public void testCondition() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
        Map<String, String> serviceMap = context.getBeansOfType(String.class);
        serviceMap.forEach((beanName, bean) -> {
            System.out.println(String.format("%s->%s", beanName, bean));
        });
    }

不会有name这个bean出现,当去掉@Conditional注解:

name->ss

案例:阻止bean的注册

上面的案例中,我们只修改配置类就行:

@Configuration
public class ConditionConfig {
	//注意,类上的注解@Conditional去掉了。
    @Conditional(MyCondition1.class)
    @Bean
    public String name1() {
        return "name1";
    }

    @Bean
    public String name2() {
        return "name2";
    }
}

运行结果:

name2->name2

Condition指定优先级

@Condtional中value指定多个Condtion的时候,默认情况下会按顺序执行

指定Condition的顺序:

自定义的Condition可以实现PriorityOrdered接口或者继承Ordered接口,或者使用@Order注解,通过这些来指定这些Condition的优先级。
排序规则:先按PriorityOrdered排序,然后按照order的值进行排序;也就是:PriorityOrdered asc,order值 asc

小伙伴可以自行测试一下,就不写案例了。

ConfigurationCondition使用

先来个案例,认识下ConfigurationCondition。

需求:有个Service的bean,和String类型的bean。当容器中有Service的Bean的时候,才能注册String类型的bean

public class Service {
}
@Configuration
public class ServiceConfig {

    @Bean
    public Service service() {
        return new Service();
    }
}
@Configuration
public class StringConfig {

    @Conditional(MyCondition.class)
    @Bean
    public String name() {
        return "BeanConfig2:name";
    }
}
public class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取spring容器
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //判断容器中是否存在Service类型的bean
        boolean existsService = !beanFactory.getBeansOfType(Service.class).isEmpty();
        return existsService;
    }
}
@Configuration
@Import({ServiceConfig.class, StringConfig.class})
public class MainConfig {

}

测试:

@Test
    public void testCondition2() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        context.getBeansOfType(String.class).forEach((beanName, bean) -> {
            System.out.println(String.format("%s->%s", beanName, bean));
        });
    }

运行:发现没有String类型的bean.

原因:Condition对配置类是有两个过程:解析和注册。而在Condition实现类的时候,解析过程,还没有发现Service类型的bean,返回false,就不会注册这个String类型的bean了。所以我们考虑使用ConfigurationCondition。

ConfigurationCondition源码:

public interface ConfigurationCondition extends Condition {

	//在什么阶段执行
	ConfigurationPhase getConfigurationPhase();


	enum ConfigurationPhase {

		//解析阶段
		PARSE_CONFIGURATION,

		//注册阶段
		REGISTER_BEAN
	}

}

我们使用ConfigurationCondition 接口,定义一个实现类:

public class MyConfigurationCondition1 implements ConfigurationCondition {

    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取spring容器
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //判断容器中是否存在Service类型的bean
        boolean existsService = !beanFactory.getBeansOfType(Service.class).isEmpty();
        return existsService;
    }
}

修改StringConfig 类:

@Configuration
public class StringConfig {

    @Conditional(MyConfigurationCondition1.class)
    @Bean
    public String name() {
        return "BeanConfig2:name";
    }
}

测试方法不变,运行,就可以得到我们想要的结果。

@Conditional注解是被这个类处理的:ConfigurationClassPostProcessor,大家还是要多撸一下这个类的源码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值