欢送拜访我的GitHub

这里分类和汇总了欣宸的全副原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本文是《quarkus依赖注入》系列的第三篇,前文咱们把握了创立bean的几种形式,本篇趁热打铁,学习一个与创立bean无关的重要知识点:一个接口如果有多个实现类时,bean实例应该如何抉择其中的一个呢?能够用注解来设定bean的抉择逻辑
  • 如果您相熟spring,此刻应该会想到ConditionalXXX注解,上面的代码来自spring官网,注解ConditionalOnProperty的作用是依据配置信息来管制bean是否实例化,本篇咱们要把握的是quarkus框架下的相似管制逻辑

    @Service@ConditionalOnProperty(value="logging.enabled", havingValue = "true", matchIfMissing = true)class LoggingService {  // ...}
  • 本篇次要是通过实例学习以下五个注解的用法
  1. LookupIfProperty,配置项的值符合要求能力应用bean
  2. LookupUnlessProperty,配置项的值不符合要求能力应用bean
  3. IfBuildProfile,如果是指定的profile能力应用bean
  4. UnlessBuildProfile,如果不是指定的profile能力应用bean
  5. IfBuildProperty,如果构建属性匹配能力应用bean

源码下载

  • 本篇实战的残缺源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
名称链接备注
我的项目主页https://github.com/zq2599/blog_demos该我的项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blog_demos.git该我的项目源码的仓库地址,https协定
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该我的项目源码的仓库地址,ssh协定
  • 这个git我的项目中有多个文件夹,本次实战的源码在<font color="blue">quarkus-tutorials</font>文件夹下,如下图红框
    <img src="https://typora-pictures-1253575040.cos.ap-guangzhou.myqcloud.com/image-20220312091203116.png" alt="image-20220312091203116" style="zoom: 80%;" />
  • <font color="blue">quarkus-tutorials</font>是个父工程,外面有多个module,本篇实战的module是<font color="red">basic-di</font>,如下图红框
    <img src="https://typora-pictures-1253575040.cos.ap-guangzhou.myqcloud.com/image-20220312091404031.png" alt="image-20220312091404031" style="zoom:80%;" />

LookupIfProperty,配置项的值符合要求能力应用bean

  • 注解<font color="blue">LookupIfProperty</font>的作用是查看指定配置项,如果存在且符合要求,能力通过代码获取到此bean,
  • 有个关键点请留神:下图是官网定义,可见LookupIfProperty并没有决定是否实例化beam,它决定的是是否通过代码取到bean,这个代码就是Instance\<T>来注入,并且用Instance.get办法来获取

  • 定义一个接口TryLookupIfProperty.java
public interface TryLookupIfProperty {    String hello();}
  • 以及两个实现类,第一个是TryLookupIfPropertyAlpha.java
public class TryLookupIfPropertyAlpha implements TryLookupIfProperty {    @Override    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 第二个TryLookupIfPropertyBeta.java
public class TryLookupIfPropertyBeta implements TryLookupIfProperty {    @Override    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 而后就是注解<font color="blue">LookupIfProperty</font>的用法了,如下所示,SelectBeanConfiguration是个配置类,外面有两个办法用来生产bean,都用注解LookupIfProperty润饰,如果配置项<font color="blue">service.alpha.enabled</font>的值等于<font color="red">true</font>,就会执行<font color="blue">tryLookupIfPropertyAlpah</font>办法,如果配置项<font color="blue">service.beta.enabled</font>的值等于<font color="red">true</font>,就会执行<font color="blue">tryLookupIfPropertyBeta</font>办法
package com.bolingcavalry.config;import com.bolingcavalry.service.TryLookupIfProperty;import com.bolingcavalry.service.impl.TryLookupIfPropertyAlpha;import com.bolingcavalry.service.impl.TryLookupIfPropertyBeta;import io.quarkus.arc.lookup.LookupIfProperty;import javax.enterprise.context.ApplicationScoped;public class SelectBeanConfiguration {    @LookupIfProperty(name = "service.alpha.enabled", stringValue = "true")    @ApplicationScoped    public TryLookupIfProperty tryLookupIfPropertyAlpha() {        return new TryLookupIfPropertyAlpha();    }    @LookupIfProperty(name = "service.beta.enabled", stringValue = "true")    @ApplicationScoped    public TryLookupIfProperty tryLookupIfPropertyBeta() {        return new TryLookupIfPropertyBeta();    }}
  • 而后来验证注解<font color="blue">LookupIfProperty</font>是否失效,上面是单元测试代码,有两处须要留神的中央,稍后会提到
package com.bolingcavalry;import com.bolingcavalry.service.TryLookupIfProperty;import com.bolingcavalry.service.impl.TryLookupIfPropertyAlpha;import io.quarkus.test.junit.QuarkusTest;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeAll;import org.junit.jupiter.api.Test;import javax.enterprise.inject.Instance;import javax.inject.Inject;@QuarkusTestpublic class BeanInstanceSwitchTest {    @BeforeAll    public static void setUp() {        System.setProperty("service.alpha.enabled", "true");    }    // 留神,后面的LookupIfProperty不能决定注入bean是否实力话,只能决定Instance.get是否能取到,    //所以此处要注入的是Instance,而不是TryLookupIfProperty自身    @Inject    Instance<TryLookupIfProperty> service;    @Test    public void testTryLookupIfProperty() {        Assertions.assertEquals("from " + tryLookupIfPropertyAlpha.class.getSimpleName(),                                service.get().hello());    }}
  • 上述代码有以下两点要留神
  1. <font color="red">留神TryLookupIfProperty的注入形式</font>,对这种运行时能力确定具体实现类的bean,要用Instance的形式注入,应用时要用Instance.get办法获得bean
  2. 单元测试的BeforeAll注解用于指定测试前要做的事件,这里用System.setProperty设置配置项<font color="blue">service.alpha.enabled</font>,所以,实践上SelectBeanConfiguration.tryLookupIfPropertyAlpha办法应该会执行,也就是说注入的TryLookupIfProperty应该是<font color="blue">TryLookupIfPropertyAlpha</font>实例,所以testTryLookupIfProperty中用assertEquals断言预测:TryLookupIfProperty.hello的值来自TryLookupIfPropertyAlpha
  • 执行单元测试,如下图,合乎预期

  • 批改BeanInstanceSwitchTest.setUp,将<font color="blue">service.alpha.enabled</font>改成<font color="red">service.alpha.enabled</font>,如此实践上SelectBeanConfiguration.tryLookupIfPropertyBeta办法应该会执行,实例化的应该就是TryLookupIfPropertyBeta,那么本次单元测试就不能通过了
  • 如下图,果然,注入的实例变成了TryLookupIfPropertyBeta,然而预期的还是之前的TryLookupIfPropertyAlpha,于是测试失败

LookupUnlessProperty,配置项的值不符合要求能力应用bean

  • <font color="blue">LookupIfProperty</font>的意思是配置项的值符合要求才会创立bean,而<font color="red">LookupUnlessProperty</font>恰好相反,意思是配置项的值<font color="red">不符合要求能力应用bean</font>
  • 为了验证LookupUnlessProperty的成果,批改<font color="blue">SelectBeanConfiguration.java</font>,只批改tryLookupIfPropertyBeta办法的注解,由从之前的LookupIfProperty改为<font color="red">LookupUnlessProperty</font>,属性也改为<font color="red">service.alpha.enabled</font>,当初的逻辑是:如果属性service.alpha.enabled的值是true,就执行tryLookupIfPropertyAlpha,如果属性service.alpha.enabled的值不是true,就执行tryLookupIfPropertyBeta
public class SelectBeanConfiguration {    @LookupIfProperty(name = "service.alpha.enabled", stringValue = "true")    @ApplicationScoped    public TryLookupIfProperty tryLookupIfPropertyAlpha() {        return new TryLookupIfPropertyAlpha();    }    @LookupUnlessProperty(name = "service.alpha.enabled", stringValue = "true")    @ApplicationScoped    public TryLookupIfProperty tryLookupIfPropertyBeta() {        return new TryLookupIfPropertyBeta();    }}
  • 关上方才的BeanInstanceSwitchTest.java,setUp办法中将<font color="blue">service.alpha.enabled</font>的值设为<font color="red">true</font>
@BeforeAllpublic static void setUp() {    System.setProperty("service.alpha.enabled", "true");}
  • 运行单元测试,如下图,合乎预期

  • 当初把<font color="blue">service.alpha.enabled</font>的值设为<font color="red">false</font>,单元测试不通过,提醒返回值是<font color="blue">TryLookupIfPropertyBeta</font>,这也是合乎预期的,证实LookupUnlessProperty曾经失效了

  • 此刻您可能会好奇,如果配置项<font color="blue">service.alpha.enabled</font>不存在会如何,咱们将setUp办法中的<font color="blue">System.setProperty</font>这段代码删除,这样配置项<font color="blue">service.alpha.enabled</font>就不存在了,再次执行单元测试,发现SelectBeanConfiguration类的tryLookupIfPropertyAlpha和tryLookupIfPropertyBeta两个办法都没有执行,导致没有TryLookupIfProperty类型的bean

  • 这时候您应该发现了一个问题:如果配置项<font color="blue">service.alpha.enabled</font>不存在的时候如何返回一个默认bean,以防止找不到bean呢?
  • LookupIfProperty和LookupUnlessProperty都有名为<font color="blue">lookupIfMissing</font>的属性,意思都一样:指定配置项不存在的时候,就执行注解所润饰的办法,批改SelectBeanConfiguration.java,如下图黄框所示,减少<font color="blue">lookupIfMissing</font>属性,指定值为true(没有指定的时候,默认值是false)

  • 再次运行单元测试,如下图,只管<font color="blue">service.alpha.enabled</font>不存在,但<font color="blue">lookupIfMissing</font>属性起了作用,SelectBeanConfiguration.tryLookupIfPropertyAlpha办法还是执行了,于是测试通过

IfBuildProfile,如果是指定的profile能力应用bean

  • 利用在运行时,其profile是固定的,<font color="blue">IfBuildProfile</font>查看以后profile是否是指定值,如果是,其润饰的bean就能被业务代码应用
  • 比照官网对LookupIfProperty和IfBuildProfile形容的差异,LookupIfProperty决定了是否能被抉择,IfBuildProfile决定了是否在容器中
# LookupIfProperty,说的是be obtained by programmaticIndicates that a bean should only be obtained by programmatic lookup if the property matches the provided value.# IfBuildProfile,说的是be enabledthe bean will only be enabled if the Quarkus build time profile matches the specified annotation value.
  • 接下来写代码验证,先写个接口
public interface TryIfBuildProfile {    String hello();}
  • 再写两个实现类,第一个是TryIfBuildProfileProd.java
public class TryIfBuildProfileProd implements TryIfBuildProfile {    @Override    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 第二个TryIfBuildProfileDefault.java
public class TryIfBuildProfileDefault implements TryIfBuildProfile {    @Override    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 再来看IfBuildProfile的用法,在方才的SelectBeanConfiguration.java中新增两个办法,如下所示,利用运行时,如果profile是<font color="blue">test</font>,那么<font color="blue">tryIfBuildProfileProd</font>办法会被执行,还要留神的是注解<font color="red">DefaultBean</font>的用法,如果profile不是<font color="blue">test</font>,那么quarkus的bean容器中就没有TryIfBuildProfile类型的bean了,此时DefaultBean润饰的tryIfBuildProfileDefault办法就会被执行,导致TryIfBuildProfileDefault的实例注册在quarkus容器中
@Produces@IfBuildProfile("test")public TryIfBuildProfile tryIfBuildProfileProd() {    return new TryIfBuildProfileProd();}@Produces@DefaultBeanpublic TryIfBuildProfile tryIfBuildProfileDefault() {    return new TryIfBuildProfileDefault();}
  • 单元测试代码写在方才的BeanInstanceSwitchTest.java中,运行单元测试是profile被设置为<font color="blue">test</font>,所以tryIfBuildProfile的预期是TryIfBuildProfileProd实例,留神,这里和后面LookupIfProperty不一样的是:这里的TryIfBuildProfile间接注入就好,不须要Instance\<T>来注入

    @InjectTryIfBuildProfile tryIfBuildProfile;@Testpublic void testTryLookupIfProperty() {    Assertions.assertEquals("from " + TryLookupIfPropertyAlpha.class.getSimpleName(),                            service.get().hello());}@Testpublic void tryIfBuildProfile() {    Assertions.assertEquals("from " + TryIfBuildProfileProd.class.getSimpleName(),                tryIfBuildProfile.hello());}
  • 执行单元测试,如下图,测试通过,红框显示以后profile的确是<font color="red">test</font>

  • 再来试试<font color="blue">DefaultBean</font>的是否失常,批改SelectBeanConfiguration.java的代码,如下图红框,将IfBuildProfile注解的值从方才的<font color="blue">test</font>改为<font color="red">prod</font>,如此一来,再执行单元测试时tryIfBuildProfileProd办法就不会被执行了,此时看tryIfBuildProfileDefault办法是否执行

  • 执行单元测试,后果如下图,黄框中的内容证实是<font color="blue">tryIfBuildProfileDefault</font>办法被执行,也就是说<font color="blue">DefaultBean</font>失常工作

UnlessBuildProfile,如果不是指定的profile能力应用bean

  • UnlessBuildProfile的逻辑与IfBuildProfile相同:如果不是指定的profile能力应用bean
  • 回顾方才测试失败的代码,如下图红框,单元测试的profile是<font color="blue">test</font>,上面要求profile必须等于<font color="red">prod</font>,因而测试失败,当初咱们将红框中的IfBuildProfile改为UnlessBuildProfile,意思是profile不等于<font color="red">prod</font>的时候bean能够应用

  • 执行单元测试,如下图,这一次顺利通过,证实UnlessBuildProfile的作用合乎预期

IfBuildProperty,如果构建属性匹配能力应用bean

  • 最初要提到注解是<font color="blue">IfBuildProperty</font>是,此注解与<font color="red">LookupIfProperty</font>相似,上面是两个注解的官网形容比照,可见IfBuildProperty作用的相熟次要是构建属性(后面的文章中提到过构建属性,它们的特点是运行期间只读,值固定不变)
# LookupIfProperty的形容,如果属性匹配,则此bean能够被获取应用Indicates that a bean should only be obtained by programmatic lookup if the property matches the provided value.# IfBuildProperty的形容,如果构建属性匹配,则此bean是enabledthe bean will only be enabled if the Quarkus build time property matches the provided value
  • 限于篇幅,就不写代码验证了,来看看官网demo,用法上与LookupIfProperty相似,能够用DefaultBean来兜底,适配匹配失败的场景
@Dependentpublic class TracerConfiguration {    @Produces    @IfBuildProperty(name = "some.tracer.enabled", stringValue = "true")    public Tracer realTracer(Reporter reporter, Configuration configuration) {        return new RealTracer(reporter, configuration);    }    @Produces    @DefaultBean    public Tracer noopTracer() {        return new NoopTracer();    }}
  • 至此,基于多种注解来抉择bean实现的学习曾经实现,依附配置项和profile,曾经能够笼罩少数场景下bean的确认,如果这些不能满足您的业务需要,接下来的文章咱们持续理解更多灵便的抉择bean的形式

欢送关注思否:程序员欣宸

学习路上,你不孤独,欣宸原创一路相伴...