欢迎访问我的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

源码下载

名称 链接 备注
项目主页 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项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框

  • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是basic-di,如下图红框

LookupIfProperty,配置项的值符合要求才能使用bean

  • 注解LookupIfProperty的作用是检查指定配置项,如果存在且符合要求,才能通过代码获取到此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();
}
}
  • 然后就是注解LookupIfProperty的用法了,如下所示,SelectBeanConfiguration是个配置类,里面有两个方法用来生产bean,都用注解LookupIfProperty修饰,如果配置项service.alpha.enabled的值等于true,就会执行tryLookupIfPropertyAlpah方法,如果配置项service.beta.enabled的值等于true,就会执行tryLookupIfPropertyBeta方法
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();
}
}
  • 然后来验证注解LookupIfProperty是否生效,下面是单元测试代码,有两处需要注意的地方,稍后会提到
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. 注意TryLookupIfProperty的注入方式,对这种运行时才能确定具体实现类的bean,要用Instance的方式注入,使用时要用Instance.get方法取得bean
  2. 单元测试的BeforeAll注解用于指定测试前要做的事情,这里用System.setProperty设置配置项service.alpha.enabled,所以,理论上SelectBeanConfiguration.tryLookupIfPropertyAlpha方法应该会执行,也就是说注入的TryLookupIfProperty应该是TryLookupIfPropertyAlpha实例,所以testTryLookupIfProperty中用assertEquals断言预测:TryLookupIfProperty.hello的值来自TryLookupIfPropertyAlpha
  • 执行单元测试,如下图,符合预期

  • 修改BeanInstanceSwitchTest.setUp,将service.alpha.enabled改成service.alpha.enabled,如此理论上SelectBeanConfiguration.tryLookupIfPropertyBeta方法应该会执行,实例化的应该就是TryLookupIfPropertyBeta,那么本次单元测试就不能通过了
  • 如下图,果然,注入的实例变成了TryLookupIfPropertyBeta,但是预期的还是之前的TryLookupIfPropertyAlpha,于是测试失败

LookupUnlessProperty,配置项的值不符合要求才能使用bean

  • LookupIfProperty的意思是配置项的值符合要求才会创建bean,而LookupUnlessProperty恰好相反,意思是配置项的值不符合要求才能使用bean

  • 为了验证LookupUnlessProperty的效果,修改SelectBeanConfiguration.java,只修改tryLookupIfPropertyBeta方法的注解,由从之前的LookupIfProperty改为LookupUnlessProperty,属性也改为service.alpha.enabled,现在的逻辑是:如果属性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方法中将service.alpha.enabled的值设为true
@BeforeAll
public static void setUp() {
System.setProperty("service.alpha.enabled", "true");
}
  • 运行单元测试,如下图,符合预期

  • 现在把service.alpha.enabled的值设为false,单元测试不通过,提示返回值是TryLookupIfPropertyBeta,这也是符合预期的,证明LookupUnlessProperty已经生效了

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

  • 这时候您应该发现了一个问题:如果配置项service.alpha.enabled不存在的时候如何返回一个默认bean,以避免找不到bean呢?

  • LookupIfProperty和LookupUnlessProperty都有名为lookupIfMissing的属性,意思都一样:指定配置项不存在的时候,就执行注解所修饰的方法,修改SelectBeanConfiguration.java,如下图黄框所示,增加lookupIfMissing属性,指定值为true(没有指定的时候,默认值是false)

  • 再次运行单元测试,如下图,尽管service.alpha.enabled不存在,但lookupIfMissing属性起了作用,SelectBeanConfiguration.tryLookupIfPropertyAlpha方法还是执行了,于是测试通过

IfBuildProfile,如果是指定的profile才能使用bean

  • 应用在运行时,其profile是固定的,IfBuildProfile检查当前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是test,那么tryIfBuildProfileProd方法会被执行,还要注意的是注解DefaultBean的用法,如果profile不是test,那么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被设置为test,所以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确实是test

  • 再来试试DefaultBean的是否正常,修改SelectBeanConfiguration.java的代码,如下图红框,将IfBuildProfile注解的值从刚才的test改为prod,如此一来,再执行单元测试时tryIfBuildProfileProd方法就不会被执行了,此时看tryIfBuildProfileDefault方法能否执行

  • 执行单元测试,结果如下图,黄框中的内容证明是tryIfBuildProfileDefault方法被执行,也就是说DefaultBean正常工作

UnlessBuildProfile,如果不是指定的profile才能使用bean

  • UnlessBuildProfile的逻辑与IfBuildProfile相反:如果不是指定的profile才能使用bean
  • 回顾刚才测试失败的代码,如下图红框,单元测试的profile是test,下面要求profile必须等于prod,因此测试失败,现在咱们将红框中的IfBuildProfile改为UnlessBuildProfile,意思是profile不等于prod的时候bean可以使用

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

IfBuildProperty,如果构建属性匹配才能使用bean

  • 最后要提到注解是IfBuildProperty是,此注解与LookupIfProperty类似,下面是两个注解的官方描述对比,可见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的方式

欢迎关注博客园:程序员欣宸

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

quarkus依赖注入之三:用注解选择注入bean的更多相关文章

  1. Spring第七弹—依赖注入之注解方式注入及编码解析@Resource原理

        注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果. 手工装配依赖对象  手工装配依赖对象,在这种方式中又有两种编 ...

  2. Spring_02 注入类型值、利用引用注入类型值、spring表达式、与类相关的注解、与依赖注入相关的注解、注解扫描

    注意:注入基本类型值在本质上就是依赖注入,而且是利用的set方式进行的依赖注入 1 注入基本类型的值 <property name="基本类型的成员变量名" value=&q ...

  3. SSM框架之Spring(3)IOC及依赖注入(基于注解的实现)

    Spring(3)IOC及依赖注入(基于注解的实现) 学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样 的,都是要降低程序间的耦合.只是配置的形 ...

  4. 002-Spring4 快速入门-项目搭建、基于注解的开发bean,Bean创建和装配、基于注解的开发bean,Bean初始化销毁、Bean装配,注解、Bean依赖注入

    一.项目搭建 1.项目创建 eclipse→project explorer→new→Project→Maven Project 默认配置即可创建项目 2.spring配置 <dependenc ...

  5. Spring Boot2(007):关于Spring beans、依赖注入 和 @SpringBootApplication 注解

    一.关于Spring beans 和 依赖注入(Dependency Injection) spring boot 和 Spring 全家桶无缝衔接,开发过程中可以很轻松地使用 Spring 全家桶的 ...

  6. spring 四种依赖注入方式以及注解注入方式

    平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程 ...

  7. EJB通过注解方式注入并使用其他EJB或者服务、配置JBoss数据源

    通过注解方式注入并使用其他EJB或者服务 真实项目EJB对象很多,EJB之间也可以互相调用, 在项目HelloWorld下新建接口Other在cn.hqu.ejb3下: public interfac ...

  8. Spring_day02--课程安排_Spring的bean管理(注解)(注解创建对象/注入属性、配置文件和注解混合使用)

    Spring_day02 上节内容回顾 今天内容介绍 Spring的bean管理(注解) 注解介绍 Spring注解开发准备 注解创建对象 注解注入属性 配置文件和注解混合使用 AOP概念 AOP原理 ...

  9. Unit02: 参数值注入 、 基于注解的组件扫描

    Unit02: 参数值注入 . 基于注解的组件扫描 (4)IOC (Inversion Of Controll 控制反转) 什么是IOC? 对象之间的依赖关系由容器来建立. 什么是DI? (Depen ...

  10. Guice 注入(@Inject注解)

    带有@Inject注解的类 使用 injector.getInstance初始化 http://blog.csdn.net/java_le/article/details/24851251 Googl ...

随机推荐

  1. KMP字符串匹配问题

    KMP算法 本文参考资料:https://www.zhihu.com/question/21923021 KMP算法是一种字符串匹配算法,可以在 \(O(n+m)\) 的时间复杂度内实现两个字符串的匹 ...

  2. java中的 \r——字符串消失了

    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) 使用Integer.parseInt( ...

  3. Nginx 面试题总结大全

    转载请注明出处: 1 介绍下nginx特点与常用模块 2 nginx特点详细 3 反向代理和正向代理 4 负载均衡策略有哪些 5 Nginx如何实现动静分离?  6 Nginx 常用命令有哪些? 7 ...

  4. 从案例中详解go-errgroup-源码

    一.背景 某次会议上发表了error group包,一个g失败,其他的g会同时失败的错误言论(看了一下源码中的一句话The first call to return a non-nil error c ...

  5. KaiOS 2.5.X 使用 WebIDE 安装以及调试第三方应用程序

    本教程将展示使用 Waterfox Classic 安装为 KaiOS 2.5.x 设计的应用,同时我们也非常推荐使用它来进行安装和调试应用. 本教程本身不会对手机产生任何损害,但请注意,你必须信任你 ...

  6. React笔记-组件通信(六)

    React笔记-组件通信(六) props概念 props是组件之间通讯的纽带 props也是组件中内置的一个属性 通过父级组件传入 在类组件里 可以直接通过this.props获取 注意: prop ...

  7. 2021-12-09:二叉树展开为链表。 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左

    2021-12-09:二叉树展开为链表. 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左 ...

  8. 《啊哈C语言——逻辑的挑战》学习笔记

    第一章 梦想启航 第1节 让计算机开口说话 1.基础知识 1)计算机"说话"的两种方式 显示在屏幕上 通过喇叭发出声音 2)计算机"说话"之显示在屏幕上 格式: ...

  9. .NET 创建无边框的跨平台应用

    .NET 创建无边框的跨平台应用 在创建了Photino应用程序以后我们发现它自带了一个标题栏,并且非常丑,我们现在要做的就是去掉这个很丑的自带标题栏,并且自定义一个更好看的,下面我们将用Masa B ...

  10. 为 Windows 系统替换优雅的苹果字体

    使用 Windows 的童鞋,大家估计都用惯了默认的微软雅黑字体,字体本身也很不错,但使用久了也该换个别的字体了,换个字体换个心情嘛. 今天给大家推荐一款非常棒的一键更换 Windows 系统字体的软 ...