quarkus依赖注入之十:学习和改变bean懒加载规则
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本篇是《quarkus依赖注入》系列的第十篇,来看一个容易被忽略的知识点:bean的懒加载,咱们先去了解quarkus框架下的懒加载规则,然后更重要的是掌握如何改变规则,以达到提前实例化的目标
- 总的来说本篇由以下内容构成
- 关于懒加载
- 编码体验懒加载
- 改变懒加载规则的第一种手段
- 改变懒加载规则的第二种手段(居然和官方资料有出入)
- 小结
关于懒加载(Lazy Instantiation)
- CDI规范下的懒加载规则:
- 常规作用域的bean(例如ApplicationScoped、RequestScoped),在注入时,实例化的是其代理类,而真实类的实例化发生在bean方法被首次调用的时候
- 伪作用域的bean(Dependent和Singleton),在注入时就会实例化
- quarkus也遵循此规则,接下来编码验证
编码验证懒加载
- 为了验证bean的懒加载,接下来会写这样一些代码
- NormalApplicationScoped.java:作用域是ApplicationScoped的bean,其构造方法中打印日志,带有自己的类名
- NormalSingleton.java:作用域是Singleton的bean,其构造方法中打印日志,带有自己的类名
- ChangeLazyLogicTest.java:这是个单元测试类,里面注入了NormalApplicationScoped和NormalSingleton的bean,在其ping方法中依次调用上面两个bean的方法
- 以上就是稍后要写的代码,咱们根据刚刚提到的懒加载规则预测一下要输出的内容和顺序:
- 首先,在ChangeLazyLogicTest的注入点,NormalSingleton会实例化,NormalApplicationScoped的代理类会实例化
- 然后,在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事件

- 运行代码前,先预测一下修改后的结果
- 首先应该是NormalApplicationScoped的实例化
- NormalApplicationScoped实例收到StarttupEvent事件,打印日志
- 开始注入bean到ChangeLazyLogicTest,引发NormalApplicationScoped代理类和NormalSingleton的实例化
- 简单地说:原本最晚实例化的NormalApplicationScoped,由于消费StarttupEvent事件,现在变成了最早实例化的
- 现在运行代码验证,如下图,符合预期

改变懒加载规则的第二种手段(居然和官方资料有出入)
- 第二种方法更简单了:用StartupEvent修饰类,下图是完整NormalApplicationScoped代码,可见改动仅有红框位置

- 在运行代码前,先预测一下运行结果,理论上应该和第一种手段的结果差不多:NormalApplicationScoped、NormalApplicationScoped代理、NormalSingleton,
- 上述推测的依据来自Startup源码中的注释,如下图,官方表示StartupEvent和Startup效果一致

官方都这么说了,我岂敢不信,不过流程还是要完成的,把修改后的代码再运行一遍,截个图贴到文中,走走过场...
然而,这次运行的结果,却让人精神一振,StartupEvent和Startup效果是不一样的!!!
运行结果如下图,最先实例化的居然不是被Startup注解修饰的NormalApplicationScoped,而是它的代理类!

- 由此可见,Startup可以将bean的实例化提前,而且是连带bean的代理类的实例化也提前了
- 回想一下,虽然结果与预期不符合,而预期来自官方注释,但这并不代表官方注释有错,人家只说了句functionally equivalent,从字面上看并不涉及代理类的实例化
- 另外Startup也有自己的独特之处,一共有以下两点
- Startup注解的value属性值,是bean的优先级,这样,多个bean都使用Startup的时候,可以通过value值设置优先级,以此控制实例化顺序(实际上控制的是事件observer的创建顺序)
- 如果一个类只有Startup注解修饰,而没有设置作用域的时候,quarkus自动将其作用域设置为ApplicationScoped,也就是说,下面这段代码中,ApplicationScoped注解写不写都一样
@ApplicationScoped
@Startup
public class NormalApplicationScoped {
小结
- 懒加载、StartupEvent、Startup这三种情况下的实例化顺序各不相同,最好是有个对比让大家一目了然,方便选择使用
- 接下来就画个对比图,图中有懒加载、StartupEvent、Startup三个场景,每个场景都是三个阶段:quarkus框架初始化、注入bean、bean的方法被调用,每个阶段都有哪些对象被实例化就是它们最大的区别,如下所示

- 至此,懒加载相关的知识点学习完毕,个人认为这是个很重要的技能,用好了它对业务有不小的助力,希望能给您一些参考吧
欢迎关注博客园:程序员欣宸
quarkus依赖注入之十:学习和改变bean懒加载规则的更多相关文章
- 【Spring源码深度解析学习系列】Bean的加载(六)
Bean的加载所涉及到的大致步骤: 1)转换对应beanName 为什么需要转换beanName呢?因为传入的参数可能是别名,也可能是FactoryBean,所以需要一系列的解析,这些解析内容包括如下 ...
- 学习EF之贪懒加载和延迟加载(2)
通过昨天对EF贪婪加载和延迟加载的学习,不难发现,延迟加载还是很好用的,但是问题也就来了,有的时候我们只需要加载一个实体,不需要和他相关的外部实体,这时候我们来看看EF延迟加载时怎么作用的吧 打开pr ...
- 提取数据库字段里面的值,并改变+图片懒加载,jquery延迟加载
要求:手机端打开某个页面的详细信息,因为网速或者别的原因,响应太慢,因为图片大的原因,希望先进来,图片在网页运行的情况再慢慢加载(jquer延迟加载) http://www.w3cways.com/1 ...
- Swift学习--闭包中的懒加载(四)
class ViewController: UIViewController { //格式:定义变量时前使用lazy来修饰变量,后面通过等到赋值一个闭包 // 注意点:1.必须是用var 2.闭包后面 ...
- 学习 | canvas实现图片懒加载 && 下滑底部加载
用canvas实现图片的懒加载并且下滑到据底部60px的时候再次加载数据,模仿UC浏览器的新闻加载. 完整代码:https://github.com/dirkhe1051931999/writeBlo ...
- vue学习指南:第十二篇(详细) - Vue的 路由 第二篇 ( 路由按需加载(懒加载))
各位朋友 因 最近工作繁忙,小编停更了一段时间,快过年了,小编祝愿 大家 事业有成 学业有成 快乐健康 2020开心过好每一天.从今天开始 我会抽时间把 Vue 的知识点补充完整,以及后期会带给大家更 ...
- jQuery延迟加载(懒加载)插件 – jquery.lazyload.js-Web前端(W3Cways.com) - Web前端学习之路
Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...
- spring源码学习之bean的加载(二)
这是接着上篇继续写bean的加载过程,好像是有点太多了,因为bean的加载过程是很复杂的,要处理的情况有很多,继续... 7.创建bean 常规的bean的创建时通过doCreateBean方法来实现 ...
- Spring5.0源码学习系列之浅谈懒加载机制原理
前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习 1.什么是懒加载? 懒加载(Lazy-ini ...
- Spring源码学习(5)—— bean的加载 part 2
之前归纳了从spring容器的缓存中直接获取bean的情况,接下来就需要从头开始bean的加载过程了.这里着重看单例的bean的加载 if(ex1.isSingleton()) { sharedIns ...
随机推荐
- 【python爬虫】bilibili综合热门页面视频图片爬取
此博客仅作为交流学习 我用python来爬取bilibili综合热门页面视频图片 首先分析页面: 如上图所示,当我们想要在页面爬取图片时,往往得不到页面图片的地址,这时我们也得不到图片 开始抓包分析: ...
- 2020-12-26:mysql中,表person有字段id、name、age、sex,id是主键,name是普通索引,age和sex没有索引。select * from person where id=1 and name='james' and age=1 and sex=0。请问这条语句有几次回表?
2020-12-26:mysql中,表person有字段id.name.age.sex,id是主键,name是普通索引,age和sex没有索引.select * from person where i ...
- dotnet平台Http消息处理者工厂
1 前言 Microsoft.Extensions.Http是一个设计非常优异的客户端工厂库,其提供了IHttpClientFactory用于创建HttpClient和IHttpMessageHand ...
- PHP反序列化常用魔术方法
PHP反序列化 php序列化(serialize):是将变量转换为可保存或传输的字符串的过程 php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用 PHP反序列 ...
- 关于建立一个Java项目全过程(专对于新手)
关于建立一个Java项目全过程 一.Java开发环境搭建 1.JDK与JRE JDK = JRE + 开发工具集(例如Javac编译工具等) JRE = JVM + Java SE标准类库 2.JDK ...
- Python socket记录
目录 网络编程 1.基本概念 Python中的网络编程 网络编程 1.基本概念 1.什么是客户端/服务器架构? 服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的"服务& ...
- 分布式数据库 Join 查询设计与实现浅析
相对于单例数据库的查询操作,分布式数据查询会有很多技术难题. 本文记录 Mysql 分库分表 和 Elasticsearch Join 查询的实现思路,了解分布式场景数据处理的设计方案. 文章从常用的 ...
- C#调用Quartz 定时任务。使用Cron表达式的方法
最近在做一个定时任务,要求是每一分钟触发一次. 由于之前是采用的FluentScheduler写的,现在改成了Cron表达式.中间出现了一些问题,所以现在写下来,和大家分享一下. 先说一下准备工作,你 ...
- CentOS7 本地光盘镜像rpm包
CentOS7 本地光盘镜像rpm包 一.前言 rpm包的下载方式 通过本地光盘镜像下载rpm,centos7.iso镜像文件,内置了绝大多数软件的rpm包(本文章即演示如何配置本地rpm) 在线下载 ...
- S32DS---make: *** No rule to make target 'clean'. Stop和make: *** No rule to make target 'all'. Stop的一个解决方法
问题: 最近在用S32DS调试代码的时候,遇到一个稀奇古怪的问题: and 折腾了半天,发现从这个页面导入工程编译就不会出现这个问题???? file-->import projects fro ...