欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本篇是《quarkus依赖注入》系列的终篇,前面十二篇已覆盖quarkus依赖注入的大部分核心内容,但依然漏掉了一些知识点,今天就将剩下的内容汇总,来个一锅端,轻松愉快的结束这个系列
- 总的来说,本篇由以下内容构成,每个段落都是个独立的知识点
- 几处可以简化编码的地方,如bean注入、构造方法等
- WithCaching:特定场景下,减少bean实例化次数
- 静态方法是否可以被拦截器拦截?
- All注解,让多个bean的注入更加直观
- 统一处理异步事件的异常
- 咱们从最简单的看起:表达方式的简化,一共有三个位置可以简化:bean的注入、bean构造方法、bean生产方法
简化之一:bean注入
-
quarkus在CDI规范的基础上做了简化,可以让我们少写几行代码
-
将配置文件中名为greeting.message的配置项注入到bean的成员变量greetingMsg中,按照CDI规范的写法如下
@Inject
@ConfigProperty(name = "greeting.message")
String greetingMsg;
- 在quarkus框架下可以略去@Inject,写成下面这样的效果和上面的代码一模一样
@ConfigProperty(name = "greeting.message")
String greetingMsg;
简化之二:bean构造方法
- 关于bean的构造方法,CDI有两个规定:首先,必须要有无参构造方法,其次,有参数的构造方法需要@Inject注解修饰,实例代码如下所示
@ApplicationScoped
public class MyCoolService {
private SimpleProcessor processor;
MyCoolService() { // dummy constructor needed
}
@Inject // constructor injection
MyCoolService(SimpleProcessor processor) {
this.processor = processor;
}
}
- 但是,在quarkus框架下,无参构造方法可不写,有参数的构造方法也可以略去@Inject,写成下面这样的效果和上面的代码一模一样
@ApplicationScoped
public class MyCoolService {
private SimpleProcessor processor;
MyCoolService(SimpleProcessor processor) {
this.processor = processor;
}
}
简化之三:bean生产方法
- 在CDI规范中,通过方法生产bean的语法如下,可见要同时使用Produces和ApplicationScoped注解修饰返回bean的方法
class Producers {
@Produces
@ApplicationScoped
MyService produceServ
ice() {
return new MyService(coolProperty);
}
}
- 在quarkus框架下可以略去@Produces,写成下面这样的效果和上面的代码一模一样
class Producers {
@ApplicationScoped
MyService produceService() {
return new MyService(coolProperty);
}
}
- 好了,热身结束,接下来看几个略有深度的技能
WithCaching注解:避免不必要的多次实例化
- 在介绍WithCaching注解之前,先来看一个普通场景
- 下面是一段单元测试代码,HelloDependent类型的bean通过Instance的方式被注入,再用Instance#get来获取此bean
@QuarkusTest
public class WithCachingTest {
@Inject
Instance<HelloDependent> instance;
@Test
public void test() {
// 第一次调用Instance#get方法
HelloDependent helloDependent = instance.get();
helloDependent.hello();
// 第二次调用Instance#get方法
helloDependent = instance.get();
helloDependent.hello();
}
}
-
上述代码是种常见的bean注入和使用方式,我们的本意是在WithCachingTest实例中多次使用HelloDependent类型的bean,可能是在test方法中使用,也可能在WithCachingTest的其他方法中使用
-
如果HelloDependent的作用域是ApplicationScoped,上述代码一切正常,但是,如果作用域是Dependent呢?代码中执行了两次Instance#get,得到的HelloDependent实例是同一个吗?Dependent的特性是每次注入都实例化一次,这里的Instance#get又算几次注入呢?
-
最简单的方法就是运行上述代码看实际效果,这里先回顾HelloDependent.java的源码,如下所示,构造方法中会打印日志,这下好办了,只要看日志出现几次,就知道实例化几次了
@Dependent
public class HelloDependent {
public HelloDependent(InjectionPoint injectionPoint) {
Log.info("injecting from bean "+ injectionPoint.getMember().getDeclaringClass());
}
public String hello() {
return this.getClass().getSimpleName();
}
}
- 运行单元测试类WithCachingTest,如下图红框所示,构造方法中的日志打印了两次,所以:每次Instance#get都相当于一次注入,如果bean的作用域是Dependent,就会创建一个新的实例并返回
- 现在问题来了:如果bean的作用域必须是Dependent,又希望多次Instance#get返回的是同一个bean实例,这样的要求可以做到吗?
- 答案是可以,用WithCaching注解修饰Instance即可,改动如下图红框1,改好后再次运行,红框2显示HelloDependent只实例化了一次
拦截静态方法
- 先回顾一下拦截器的基本知识,定义一个拦截器并用来拦截bean中的方法,总共需要完成以下三步
- 实现拦截器的具体功能时,还要用注解指明拦截器类型,一共有四种类型
- AroundInvoke:拦截bean方法
- PostCons