@Transactional +自定义注解不生效_研磨Spring条件注入基于@Conditional注解

在Spring项目中,我们希望bean的注入不是必须的,而是依赖条件的。

只有当项目中引入特定依赖库、或者只有当某个bean被创建、或者设置了某个环境变量时,才会创建这个bean。

在Spring4之前,这种条件注入的方式还不支持,在Spring4之后引入了一个新的注解 @Conditional ,这个注解作用在@Bean注解修饰的方式上。它能够通过判断指定条件是否满足来决定是否创建这样的Bean。

使用@Conditional注解需要满足一定条件:

@Conditional注解的类要实现Condition接口,它提供了一个matches()方法。只有matches()方法返回true时, 则被@Conditional注解修饰的bean就会被创建出来,否则不会创建(即matches方法返回false)。

接下来,我们对@Conditionl注解进行深入探讨。

@Conditional分析

@Conditional是Spring4提供的新注解,源码如下:

95fa74b43fb97d7744d25ec1ea8d0514.png

可以看出,注解被用于标注类和方法,在运行时生效。

通过value()方法可以看出,它要求传入一个Class数组,并且需要继承Condition接口。

我们接着看下Condition接口源码。

Condition接口

55bc3265fe23da357093764ebcd88397.png

业务类需要实现matches方法,返回true则对@Conditional标注的bean进行注入,否则不注入。这就是所谓的条件注入

这里注意,matches() 方法参数 ConditionContext 为 Condition设计的接口类,调用者能够从中获取到Spring容器的以下信息:

12723ac21f721205d6fbc8f03032106e.png

我们写一个demo来对@Conditional进行更为直观的展示。

@Conditional实战样例展示

首先定一个Bean,作为条件注入的目标对象。当注解生效则注入该bean,否则不予注入。

73794971233b3840705673bb546cccb0.png

我们定义一个电脑pojo类。

接着创建一个BeanConfig类,注入两个Computer实例,作为测试的基准。

baca5d05a20b22ec678380dbc65f5ffb.png

这里我们创建了两个Computer的实例(微星、戴尔),并为其设置名称与价格。

编写一个测试方法check一下是否成功注入了bean。

a69d4e3330777d4dadbf49df6ad37cfb.png

demo工程使用spring2.2.1进行构建,我们尝试在main方法中通过bName的方式获取两个注入的bean。运行结果如下:

c5f4ade7483b901e0e23c8e30dc20af9.png

可以看到到目前为止bean是成功注入的,这种方式为静态注入。接着我们就编写代码实现条件注入

首先我们定义一个场景,在不同的环境下,注入不同的Computer实例,如:dev环境下注入msi(微星),prod下注入dell(戴尔),该如何实现呢?

我们的思路是根据环境变量中设置的env参数的不同,选择不同的bean进行注入,即:

env=dev,  注入msi实例

env=prod,注入dell实例

这里就需要请@Conditional一显身手了。首先我们需要实现Condition接口。

实现Condition接口

这里需要分别实现dev、prod下的两个condition实现类。

DevCondition(开发环境Condition实现)

063696f97ce418b7e861540e8835d045.png

ProdCondition(生产环境Condition实现)

2a06df7dfc39de6a0f5d2d304634da9b.png

我们在上文中已经对matches方法的两个参数的含义进行了解释。这里需要注意的是,matches方法参数中的conditionContext提供了多个方法,方便获取Bean的各种信息。

这些方法也是SpringBoot中派生注解@ConditonalOnXX的基础。

我们接下来就使用这两个Condition的实现类对上面的例子进行修改。

修改BeanConfig,为msi标注DevCondition,为dell标注ProdCondition。并为启动类配置env环境变量,笔者使用的是IDEA开发环境,因此在

Run->Edit runconfigurations 中编辑环境变量即可。

首先设置env=dev,修改启动类测试代码如下:

7ed59659a4ac07aa619e5fe2142a6447.png

我们尝试加载Computer所有的实例,并进行打印。运行结果如下:

b62352cc2aa3a3c9565e3e156ac55040.png

只有msi实例加载,这符合我们的预期。

注入Condition实例的数组

我们注意到,@Conditional注解传入的是一个继承了Condition接口的Class数组。也就是说,我们完全可以在@Conditional注解的values中设置一个数组,传入多个Condition实现类,确保在所有的条件都满足才进行bean注入。

编写一个新的Condition实现类,matches方法返回true:

73131940f12c94f4033cad9b9830c89e.png

修改BeanConfig类,msi这个bean的@Conditional注解中增加DefaultCondition。

e78e73e775f7f27f3f60b67627cb71df.png

再次运行测试方法,返回如下:

1b79839423b619dd5859317017050f3e.png

可以看到,当多个condition返回均为true时,bean被注入了。我们修改DefaultCondition的matches方法返回false,再次运行测试方法,返回结果:

97fc23b8cd2c70e0f352155203893b75.png

可以看到,当有一个Condition返回false,则bean就不会被注入。这有点像逻辑运算下的“逻辑与”。这种方式支持我们在复杂条件下对bean进行注入的要求。

ps: @Conditional注解在方法上,只能注入一个实例;如果注解在类上,则当前类下的所有bean实例都能够被注入。这里就不进行测试了,感兴趣的同学可以自行尝试。

小结

本文我们主要了解了@Conditional注解的原理及其使用方法,并且知道了该注解是Spring Boot条件注入的基础。

在后续开发中如果遇到需要根据某个条件来决定Bean注入的场景,我们首先就应该想到Spring为我们提供的@Conditional注解,并且能够准确的加以应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值