乐趣区

关于后端:quarkus依赖注入之十学习和改变bean懒加载规则

欢送拜访我的 GitHub

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

本篇概览

  • 本篇是《quarkus 依赖注入》系列的第十篇,来看一个容易被疏忽的知识点:bean 的懒加载,咱们先去理解 quarkus 框架下的懒加载规定,而后更重要的是把握如何扭转规定,以达到提前实例化的指标
  • 总的来说本篇由以下内容形成
  • 对于懒加载
  • 编码体验懒加载
  • 扭转懒加载规定的第一种伎俩
  • 扭转懒加载规定的第二种伎俩(竟然和官网材料有出入)
  • 小结

对于懒加载(Lazy Instantiation

  • CDI 标准下的懒加载规定:
  • 惯例作用域 的 bean(例如 ApplicationScoped、RequestScoped),在注入时,实例化的是其代理类,而实在类的实例化产生在 bean 办法被首次调用的时候
  • 伪作用域 的 bean(Dependent 和 Singleton),在注入时就会实例化
  • quarkus 也遵循此规定,接下来编码验证

编码验证懒加载

  • 为了验证 bean 的懒加载,接下来会写这样一些代码
  1. NormalApplicationScoped.java:作用域是 ApplicationScoped 的 bean,其构造方法中打印日志,带有本人的类名
  2. NormalSingleton.java:作用域是 Singleton 的 bean,其构造方法中打印日志,带有本人的类名
  3. ChangeLazyLogicTest.java:这是个单元测试类,外面注入了 NormalApplicationScoped 和 NormalSingleton 的 bean,在其 ping 办法中顺次调用下面两个 bean 的办法
  • 以上就是稍后要写的代码,咱们依据刚刚提到的懒加载规定预测一下要输入的内容和程序:
  1. 首先,在 ChangeLazyLogicTest 的注入点,NormalSingleton 会实例化,NormalApplicationScoped 的代理类会实例化
  2. 而后,在 ChangeLazyLogicTest#ping 办法中,因为调用了 NormalApplicationScoped 的办法,会导致 NormalApplicationScoped 的实例化
  • 接下来开始写代码,第一个 bean,NormalApplicationScoped.java
package com.bolingcavalry;

import com.bolingcavalry.service.impl.NormalApplicationScoped;
import com.bolingcavalry.service.impl.NormalSingleton;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;

@QuarkusTest
class ChangeLazyLogicTest {

    @Inject
    NormalSingleton normalSingleton;

    @Inject
    NormalApplicationScoped normalApplicationScoped;

    @Test
    void ping() {Log.info("start invoke normalSingleton.ping");
        normalSingleton.ping();
        Log.info("start invoke normalApplicationScoped.ping");
        normalApplicationScoped.ping();}
}
  • 第二个 bean,NormalSingleton.java
package com.bolingcavalry.service.impl;

import io.quarkus.logging.Log;
import javax.inject.Singleton;

@Singleton
public class NormalSingleton {public NormalSingleton() {Log.info("Construction from" + this.getClass().getSimpleName());
    }

    public String ping() {return "ping from NormalSingleton";}
}
  • 而后是单元测试类 ChangeLazyLogicTest,可见 NormalApplicationScoped 构造方法的日志应该在 start invoke normalApplicationScoped.ping 这一段之后
package com.bolingcavalry;

import com.bolingcavalry.service.impl.NormalApplicationScoped;
import com.bolingcavalry.service.impl.NormalSingleton;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;

@QuarkusTest
class ChangeLazyLogicTest {

    @Inject
    NormalSingleton normalSingleton;

    @Inject
    NormalApplicationScoped normalApplicationScoped;

    @Test
    void ping() {Log.info("start invoke normalSingleton.ping");
        normalSingleton.ping();
        Log.info("start invoke normalApplicationScoped.ping");
        normalApplicationScoped.ping();}
}
  • 编码实现,运行单元测试类,验证咱们之前的预测,控制台输入后果如下图所示,合乎预期
  • 至此,懒加载根本规定咱们曾经分明了,聪慧的您应该想到了此规定的弊病:如果在构造方法中有一些耗时操作,必须等到第一次调用 bean 的办法时才会执行,这可能不合乎咱们的预期,有时候咱们心愿利用初始化的时候把耗时的事件做完,这样执行 bean 办法的时候就没有影响了
  • 显然,quarkus 也意识到了这个问题,于是,给出了两中扭转懒加载规定的办法,使得 bean 的实例化能够更早实现,接下来咱们一一尝试

扭转懒加载规定的第一种伎俩

  • 让 bean 尽早实例化的第一种伎俩,是让 bean 生产 StartupEvent 事件,这是 quarkus 框架启动胜利后收回的事件,从工夫上来看,此事件的工夫比注入 bean 的工夫还要早,这样生产事件的 bean 就会实例化
  • 咱们给 NormalApplicationScoped 减少下图红框中的代码,让它生产 StartupEvent 事件
  • 运行代码前,先预测一下批改后的后果
  1. 首先应该是 NormalApplicationScoped 的实例化
  2. NormalApplicationScoped 实例收到 StarttupEvent 事件,打印日志
  3. 开始注入 bean 到 ChangeLazyLogicTest,引发 NormalApplicationScoped 代理类和 NormalSingleton 的实例化
  4. 简略地说:本来最晚实例化的 NormalApplicationScoped,因为生产 StarttupEvent 事件,当初变成了最早实例化的
  • 当初运行代码验证,如下图,合乎预期

扭转懒加载规定的第二种伎俩(竟然和官网材料有出入)

  • 第二种办法更简略了:用 StartupEvent 润饰类,下图是残缺 NormalApplicationScoped 代码,可见改变仅有红框地位
  • 在运行代码前,先预测一下运行后果,实践上应该和第一种伎俩的后果差不多:NormalApplicationScoped、NormalApplicationScoped 代理、NormalSingleton,
  • 上述揣测的根据来自 Startup 源码中的正文,如下图,官网示意 StartupEvent 和 Startup 成果统一
  • 官网都这么说了,我岂敢不信,不过流程还是要实现的,把批改后的代码再运行一遍,截个图贴到文中,走走过场 …
  • 然而,这次运行的后果,却让人精力一振,StartupEvent 和 Startup 成果是不一样的!!!
  • 运行后果如下图,最先实例化的竟然不是被 Startup 注解润饰的 NormalApplicationScoped,而是它的代理类!
  • 由此可见,Startup 能够将 bean 的实例化提前,而且是连带 bean 的代理类的实例化也提前了
  • 回忆一下,尽管后果与预期不合乎,而预期来自官网正文,但这并不代表官网正文有错,人家只说了句functionally equivalent,从字面上看并不波及代理类的实例化
  • 另外 Startup 也有本人的独特之处,一共有以下两点
  1. Startup 注解的 value 属性值,是 bean 的优先级,这样,多个 bean 都应用 Startup 的时候,能够通过 value 值设置优先级,以此管制实例化程序(实际上管制的是事件 observer 的创立程序)
  2. 如果一个类只有 Startup 注解润饰,而没有设置作用域的时候,quarkus 主动将其作用域设置为ApplicationScoped,也就是说,上面这段代码中,ApplicationScoped 注解写不写都一样
@ApplicationScoped
@Startup
public class NormalApplicationScoped {

小结

  • 懒加载、StartupEvent、Startup 这三种状况下的实例化程序各不相同,最好是有个比照让大家高深莫测,不便抉择应用
  • 接下来就画个比照图,图中有懒加载、StartupEvent、Startup 三个场景,每个场景都是三个阶段:quarkus 框架初始化、注入 bean、bean 的办法被调用,每个阶段都有哪些对象被实例化就是它们最大的区别,如下所示
  • 至此,懒加载相干的知识点学习结束,集体认为这是个很重要的技能,用好了它对业务有不小的助力,心愿能给您一些参考吧

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

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

退出移动版