欢迎访问我的GitHub

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

本篇概览

  • 本篇是《quarkus依赖注入》系列的第十篇,来看一个容易被忽略的知识点:bean的懒加载,咱们先去了解quarkus框架下的懒加载规则,然后更重要的是掌握如何改变规则,以达到提前实例化的目标
  • 总的来说本篇由以下内容构成
  1. 关于懒加载
  2. 编码体验懒加载
  3. 改变懒加载规则的第一种手段
  4. 改变懒加载规则的第二种手段(居然和官方资料有出入)
  5. 小结

关于懒加载(Lazy Instantiation

  • CDI规范下的懒加载规则:
  1. 常规作用域的bean(例如ApplicationScoped、RequestScoped),在注入时,实例化的是其代理类,而真实类的实例化发生在bean方法被首次调用的时候
  2. 伪作用域的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的方法被调用,每个阶段都有哪些对象被实例化就是它们最大的区别,如下所示

  • 至此,懒加载相关的知识点学习完毕,个人认为这是个很重要的技能,用好了它对业务有不小的助力,希望能给您一些参考吧

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

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

quarkus依赖注入之十:学习和改变bean懒加载规则的更多相关文章

  1. 【Spring源码深度解析学习系列】Bean的加载(六)

    Bean的加载所涉及到的大致步骤: 1)转换对应beanName 为什么需要转换beanName呢?因为传入的参数可能是别名,也可能是FactoryBean,所以需要一系列的解析,这些解析内容包括如下 ...

  2. 学习EF之贪懒加载和延迟加载(2)

    通过昨天对EF贪婪加载和延迟加载的学习,不难发现,延迟加载还是很好用的,但是问题也就来了,有的时候我们只需要加载一个实体,不需要和他相关的外部实体,这时候我们来看看EF延迟加载时怎么作用的吧 打开pr ...

  3. 提取数据库字段里面的值,并改变+图片懒加载,jquery延迟加载

    要求:手机端打开某个页面的详细信息,因为网速或者别的原因,响应太慢,因为图片大的原因,希望先进来,图片在网页运行的情况再慢慢加载(jquer延迟加载) http://www.w3cways.com/1 ...

  4. Swift学习--闭包中的懒加载(四)

    class ViewController: UIViewController { //格式:定义变量时前使用lazy来修饰变量,后面通过等到赋值一个闭包 // 注意点:1.必须是用var 2.闭包后面 ...

  5. 学习 | canvas实现图片懒加载 && 下滑底部加载

    用canvas实现图片的懒加载并且下滑到据底部60px的时候再次加载数据,模仿UC浏览器的新闻加载. 完整代码:https://github.com/dirkhe1051931999/writeBlo ...

  6. vue学习指南:第十二篇(详细) - Vue的 路由 第二篇 ( 路由按需加载(懒加载))

    各位朋友 因 最近工作繁忙,小编停更了一段时间,快过年了,小编祝愿 大家 事业有成 学业有成 快乐健康 2020开心过好每一天.从今天开始 我会抽时间把 Vue 的知识点补充完整,以及后期会带给大家更 ...

  7. jQuery延迟加载(懒加载)插件 – jquery.lazyload.js-Web前端(W3Cways.com) - Web前端学习之路

    Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...

  8. spring源码学习之bean的加载(二)

    这是接着上篇继续写bean的加载过程,好像是有点太多了,因为bean的加载过程是很复杂的,要处理的情况有很多,继续... 7.创建bean 常规的bean的创建时通过doCreateBean方法来实现 ...

  9. Spring5.0源码学习系列之浅谈懒加载机制原理

    前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习 1.什么是懒加载? 懒加载(Lazy-ini ...

  10. Spring源码学习(5)—— bean的加载 part 2

    之前归纳了从spring容器的缓存中直接获取bean的情况,接下来就需要从头开始bean的加载过程了.这里着重看单例的bean的加载 if(ex1.isSingleton()) { sharedIns ...

随机推荐

  1. 2023-03-26:给定一个二维数组matrix, 每个格子都是正数,每个格子都和上、下、左、右相邻。 你可以从任何一个格子出发,走向相邻的格子, 把沿途的数字乘起来,希望得到的最终数字中,结尾的0

    2023-03-26:给定一个二维数组matrix, 每个格子都是正数,每个格子都和上.下.左.右相邻. 你可以从任何一个格子出发,走向相邻的格子, 把沿途的数字乘起来,希望得到的最终数字中,结尾的0 ...

  2. golang技术栈常见网址

    go所有,包含goadmin golang标准库文档 golang修养之路 Golang Profiling: 关于 pprof go问题 go语言设计与实现 go.mod解析 proto3 prot ...

  3. 2021-08-09:给定一个有正、有负、有0的数组arr,给定一个整数k,返回arr的子集是否能累加出k。1)正常怎么做?2)如果arr中的数值很大,但是arr的长度不大,怎么做?

    2021-08-09:给定一个有正.有负.有0的数组arr,给定一个整数k,返回arr的子集是否能累加出k.1)正常怎么做?2)如果arr中的数值很大,但是arr的长度不大,怎么做? 福大大 答案20 ...

  4. 【C++】初始化列表构造函数VS普通构造函数

    普通构造函数VS初始化列表构造函数 初始化列表构造函数最优先匹配问题 对于一个类而言,只要其中包含有初始化列表的构造函数,编译器在编译使用{}语法的构造时会最倾向于调用初始化列表构造函数,哪怕做类型转 ...

  5. 2013年蓝桥杯C/C++大学A组省赛真题(排它平方数)

    题目描述: 小明正看着 203879 这个数字发呆. 原来,203879 * 203879 = 41566646641 这有什么神奇呢?仔细观察,203879 是个6位数,并且它的每个数位上的数字都是 ...

  6. 统信UOS系统开发笔记(三):从Qt源码编译安装之编译安装Qt5.12.8

    前言   上一篇,是使用Qt提供的安装包安装的,有些场景需要使用到自己编译的Qt,所以本篇如何在统信UOS系统上编译Qt5.12.8源码.   统信UOS系统版本   系统版本:   Qt源码下载   ...

  7. Access+Trunk 配置

    实验拓扑 实验需求 按图示给各 PC 配置 IP 地址 在SW1和SW2上创建聚合组 PC1 与 PC3 属于 VLAN 10 : PC2 与 PC4 属于 VLAN 20 利用 Access 与 T ...

  8. idea设置字体大小(换主题后的字体大小设置)

    如果你是默认主题 直接这样设置字体大小 如果你换了自定义主题 如果你换了自定义主题,那么上面的设置方法会没有作用,我们需要像下面这样设置:

  9. 【Oracle】使用PL/SQL实现冒泡排序

    [Oracle]使用PL/SQL实现冒泡排序 一般来说,SQL要排序的话直接使用order by即可 不一般来说,就是瞎搞,正好也可以巩固自己的数据结构基础 存储包内容如下 规范: create or ...

  10. 每日一题 力扣 445 https://leetcode.cn/problems/add-two-numbers-ii/

    可以直接用栈去做就行,逆序想到栈的做法 然后算完一个就直接赋值给答案数组  我用的是常见 public ListNode addTwoNumbers(ListNode l1, ListNode l2) ...