关于后端:quarkus依赖注入之三用注解选择注入bean

44次阅读

共计 10732 个字符,预计需要花费 27 分钟才能阅读完成。

欢送拜访我的 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;

@QuarkusTest
public 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>
@BeforeAll
public 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 programmatic
Indicates that a bean should only be obtained by programmatic lookup if the property matches the provided value.
# IfBuildProfile,说的是 be enabled
the 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
@DefaultBean
public TryIfBuildProfile tryIfBuildProfileDefault() {return new TryIfBuildProfileDefault();
}
  • 单元测试代码写在方才的 BeanInstanceSwitchTest.java 中,运行单元测试是 profile 被设置为 <font color=”blue”>test</font>,所以 tryIfBuildProfile 的预期是 TryIfBuildProfileProd 实例,留神,这里和后面 LookupIfProperty 不一样的是:这里的 TryIfBuildProfile 间接注入就好,不须要 Instance\<T> 来注入

    @Inject
    TryIfBuildProfile tryIfBuildProfile;
    
    @Test
    public void testTryLookupIfProperty() {Assertions.assertEquals("from" + TryLookupIfPropertyAlpha.class.getSimpleName(),
                                service.get().hello());
    }
    
    @Test
    public 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 是 enabled
the bean will only be enabled if the Quarkus build time property matches the provided value
  • 限于篇幅,就不写代码验证了,来看看官网 demo,用法上与 LookupIfProperty 相似,能够用 DefaultBean 来兜底,适配匹配失败的场景
@Dependent
public 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 的形式

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

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

正文完
 0