quarkus依赖注入之七:生命周期回调
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本篇的知识点是bean的生命周期回调:在bean生命周期的不同阶段,都可以触发自定义代码的执行
- 触发自定义代码执行的具体方式,是用对应的注解去修饰要执行的方法,如下图所示:

- 有两种模式可以实现生命周期回调:拦截器模式和自定义模式,接下来通过编码依次学习
拦截器模式
- 《拦截器(Interceptor)》已详细介绍了quarkus拦截器的自定义和使用,包括以下三个步骤

- 如果要自定义bean的生命周期回调,也是遵照上述步骤执行,接下来编码实现
- 首先定义拦截器,名为TrackLifeCycle,就是个普通拦截器,需要用注解InterceptorBinding修饰
package com.bolingcavalry.interceptor.define;
import javax.interceptor.InterceptorBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
@InterceptorBinding
@Target({TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackLifeCycle {
}
- 然后是实现拦截器的功能,有几处要注意的地方稍后会提到
package com.bolingcavalry.interceptor.impl;
import com.bolingcavalry.interceptor.define.TrackLifeCycle;
import io.quarkus.arc.Priority;
import io.quarkus.logging.Log;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.interceptor.AroundConstruct;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@TrackLifeCycle
@Interceptor
@Priority(Interceptor.Priority.APPLICATION + 1)
public class LifeCycleInterceptor {
@AroundConstruct
void execute(InvocationContext context) throws Exception {
Log.info("start AroundConstruct");
try {
context.proceed();
} catch (Exception e) {
e.printStackTrace();
}
Log.info("end AroundConstruct");
}
@PostConstruct
public void doPostConstruct(InvocationContext ctx) {
Log.info("life cycle PostConstruct");
}
@PreDestroy
public void doPreDestroy(InvocationContext ctx) {
Log.info("life cycle PreDestroy");
}
}
- 上述代码有以下几点需要注意
- 用注解Interceptor和TrackLifeCycle修饰,说明这是拦截器TrackLifeCycle的实现
- 被拦截bean实例化的时候,AroundConstruct修饰的方法execute就会被执行,这和《拦截器》一文中的AroundInvoke的用法很相似
- 被拦截bean创建成功后,PostConstruct修饰的方法doPostConstruct就会被执行
- 被拦截bean在销毁之前,PreDestroy修饰的方法doPreDestroy就会被执行
- 接下来是使用拦截器TrackLifeCycle了,用于演示的bean如下,用TrackLifeCycle修饰,有构造方法和简单的helloWorld方法
@ApplicationScoped
@TrackLifeCycle
public class Hello {
public Hello() {
Log.info(this.getClass().getSimpleName() + " at instance");
}
public void helloWorld() {
Log.info("Hello world!");
}
}
- 最后再写个单元测试类验证
@QuarkusTest
public class LifeCycleTest {
@Inject
Hello hello;
@Test
public void testLifyCycle() {
hello.helloWorld();
}
}
- 执行单元测试,控制台输出如下,可见拦截器的日志输出都符合预期
15:26:32,447 INFO [io.quarkus] (main) Quarkus 2.7.3.Final on JVM started in 2.899s. Listening on: http://localhost:8081
15:26:32,448 INFO [io.quarkus] (main) Profile test activated.
15:26:32,448 INFO [io.quarkus] (main) Installed features: [agroal, cdi, narayana-jta, resteasy, smallrye-context-propagation, vertx]
15:26:32,483 INFO [com.bol.lif.Hello] (main) Hello_ClientProxy at instance
15:26:33,040 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) start AroundConstruct
15:26:33,040 INFO [com.bol.lif.Hello] (main) Hello_Subclass at instance
15:26:33,040 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) end AroundConstruct
15:26:33,041 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PostConstruct
15:26:33,041 INFO [com.bol.lif.Hello] (main) Hello world!
15:26:33,097 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PreDestroy
15:26:33,128 INFO [io.quarkus] (main) Quarkus stopped in 0.075s
- 以上就是通过拦截器制作的bean生命周期回调的全过程,接下来再看另一种方式:不用拦截器的方式
自定义模式
- 刚才的拦截器模式有个明显问题:如果不同bean的生命周期回调有不同业务需求,该如何是好?为每个bean做一个拦截器吗?随着bean的增加会有大量拦截器,似乎不是个好的方案
- 如果您熟悉spring,对下面的代码要改不陌生,这是来自spring官网的内容,直接在bean的方法上用PostConstruct和PreDestroy修饰,即可在bean的创建完成和销毁前被调用
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}
- 实际上,quarkus也支持上述方式,不过和拦截器相比有两个差异:
- 在bean的内部,只能用PostConstruct和TrackLifeCycle,不能用AroundConstruct,只有拦截器才能用AroundConstruct
- 在拦截器中,PostConstruct和TrackLifeCycle修饰的方法必须要有InvocationContext类型的入参,但是在bean内部则没有此要求
- 咱们来改造Hello.java的源码,修改后如下,增加了两个方法,分别被PostConstruct和PreDestroy修饰
@ApplicationScoped
@TrackLifeCycle
public class Hello {
public Hello() {
Log.info(this.getClass().getSimpleName() + " at instance");
}
@PostConstruct
public void doPostConstruct() {
Log.info("at doPostConstruct");
}
@PreDestroy
public void doPreDestroy() {
Log.info("at PreDestroy");
}
public void helloWorld() {
Log.info("Hello world!");
}
}
- 再次运行单元测试,控制台输出如下,可见Hello自定义的两个生命周期回调都执行了,同时原拦截器的三个回调也都正常执行
16:27:54,134 INFO [io.quarkus] (main) Quarkus 2.7.3.Final on JVM started in 2.529s. Listening on: http://localhost:8081
16:27:54,135 INFO [io.quarkus] (main) Profile test activated.
16:27:54,135 INFO [io.quarkus] (main) Installed features: [agroal, cdi, narayana-jta, resteasy, smallrye-context-propagation, vertx]
16:27:54,147 INFO [com.bol.lif.Hello] (main) Hello_ClientProxy at instance
16:27:54,710 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) start AroundConstruct
16:27:54,711 INFO [com.bol.lif.Hello] (main) Hello_Subclass at instance
16:27:54,711 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) end AroundConstruct
16:27:54,711 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PostConstruct
16:27:54,712 INFO [com.bol.lif.Hello] (main) at doPostConstruct
16:27:54,712 INFO [com.bol.lif.Hello] (main) Hello world!
16:27:54,747 INFO [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PreDestroy
16:27:54,747 INFO [com.bol.lif.Hello] (main) at PreDestroy
16:27:54,765 INFO [io.quarkus] (main) Quarkus stopped in 0.044s
dispose注解:实现销毁前自定义操作,dispose是另一种可选方案
- 试想这样的场景:我的bean在销毁前要做自定义操作,但是如果用之前的两种方案,可能面临以下问题:
- 不适合修改bean的代码,bean的类可能是第三方库
- 也不适合修改生命周期拦截器代码,拦截器可能也是第三方库,也可能是多个bean共用,若修改会影响其他bean
- 好在quarkus为我们提供了另一个方案,不用修改bean和拦截器的代码,用注解dispose修饰指定方法即可,接下来编码验证
- 增加一个普通类ResourceManager.java,假设这是业务中的资源管理服务,可以打开和关闭业务资源,稍后会在配置类中将其指定为bean
package com.bolingcavalry.service.impl;
import io.quarkus.logging.Log;
/**
* @author zq2599@gmail.com
* @Title: 资源管理类
* @Package
* @Description:
* @date 4/10/22 10:20 AM
*/
public class ResourceManager {
public ResourceManager () {
Log.info("create instance, " + this.getClass().getSimpleName());
}
/**
* 假设再次方法中打开资源,如网络、文件、数据库等
*/
public void open() {
Log.info("open resource here");
}
/**
* 假设在此方法中关闭所有已打开的资源
*/
public void closeAll() {
Log.info("close all resource here");
}
}
- 配置类SelectBeanConfiguration.java,指定了ResourceManager的生命周期是每次http请求
package com.bolingcavalry.config;
import com.bolingcavalry.service.impl.ResourceManager;
import javax.enterprise.context.RequestScoped;
public class SelectBeanConfiguration {
@RequestScoped
public ResourceManager getResourceManager() {
return new ResourceManager();
}
}
- 再写一个web服务类ResourceManagerController.java,这里面使用了ResourceManager
package com.bolingcavalry;
import com.bolingcavalry.service.impl.ResourceManager;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/resourcemanager")
public class ResourceManagerController {
@Inject
ResourceManager resourceManager;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String get() {
resourceManager.open();
return "success";
}
}
- 由于ResourceManager的生命周期是RequestScoped,因此每次请求/resourcemanager都会实例化一个ResourceManager,请求结束后再将其销毁
- 现在,业务需求是每个ResourceManager的bean在销毁前,都要求其closeAll方法被执行
- 重点来了,在SelectBeanConfiguration.java中新增一个方法,入参是bean,而且要用Disposes注解修饰,如此,ResourceManager类型的bean在销毁前此方法都会被执行
/**
* 使用了Disposes注解后,ResourceManager类型的bean在销毁前,此方法都会执行
* @param resourceManager
*/
public void closeResource(@Disposes ResourceManager resourceManager) {
// 在这里可以做一些额外的操作,不需要bean参与
Log.info("do other things that bean do not care");
// 也可以执行bean的方法
resourceManager.closeAll();
}
- 最后是单元测试类DisposeTest.java,这里用了注解RepeatedTest表示重复执行,属性值为3,表示重复执行3次
@QuarkusTest
public class DisposeTest {
@RepeatedTest(3)
public void test() {
given()
.when().get("/resourcemanager")
.then()
.statusCode(200)
// 检查body内容
.body(is("success"));
}
}
- 执行单元测试,控制台输出如下图,可见每次请求都有bean创建,也伴随着bean销毁,每次销毁都会执行closeResource方法,符合预期

- 至此,生命周期回调相关的实战就完成了,希望能给您一些参考,接下来的文章会继续深入学习依赖注入相关的知识点
欢迎关注博客园:程序员欣宸
quarkus依赖注入之七:生命周期回调的更多相关文章
- spring注解(Component、依赖注入、生命周期、作用域)
1.注解 注解就是一个类,使用@加上注解名称,开发中可以使用注解取代配置文件 2.@Component 取代<bean class="">,@Component 取代 ...
- NetCore实例提供的依赖注入的生命周期
Transient: 每一次GetService都会创建一个新的实例,每次从容器 (IServiceProvider)中获取的时候都是一个新的实例Scoped: 在同一个Scope内只初始化一个实例 ...
- .NET Core 中三种模式依赖注入的生命周期。
注入模式 同一个请求作用域 不同的请求作用域 AddSingleton 同一个实例 同一个实例 AddScoped 同一个实例 新实例 AddTransient 新实例 新实例
- Bean 注解(Annotation)配置(2)- Bean作用域与生命周期回调方法配置
Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...
- Bean XML 配置(2)- Bean作用域与生命周期回调方法配置
系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Contro ...
- Vuex 注入 Vue 生命周期的过程
首先我们结合 Vue 和 Vuex 的部分源码,来说明 Vuex 注入 Vue 生命周期的过程. 说到源码,其实没有想象的那么难.也和我们平时写业务代码差不多,都是方法的调用.但是源码的调用树会复杂很 ...
- Cocos Creator学习三:生命周期回调函数
1.目的:学习生命周期回调函数以及回调顺序,更有利于我们逻辑的处理把控. 2.生命周期回调函数: 节点:指cc.Node:组件:指cc.Component. ①onLoad:脚本组件绑定的节点所在场景 ...
- 如何解决微信小程序界面适配问题-引用-生命周期回调函数-优化机制-样式引入
如何解决微信小程序界面适配问题 .wxss page{ height: 100%; width:750rpx; } this.setData({ imageWidth: wx.getSystemInf ...
- pomelo生命周期回调和组件加入
一 生命周期回调 生命周期回调可以让开发人员在不同类型的server生命周期中进行详细操作. 提供的生命周期回调函数包含:beforeStartup,afterStartup,beforeShutdo ...
- .Net核心依赖项注入:生命周期和最佳实践
在讨论.Net的依赖注入(DI)之前,我们需要知道我们为什么需要使用依赖注入 依赖反转原理(DIP): DIP允许您将两个类解耦,否则它们会紧密耦合,这有助于提高可重用性和更好的可维护性 DIP介绍: ...
随机推荐
- “StackLLaMA”: 用 RLHF 训练 LLaMA 的手把手教程
如 ChatGPT,GPT-4,Claude语言模型 之强大,因为它们采用了 基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback, RLHF ...
- Django笔记三十八之发送邮件
本文首发于公众号:Hunter后端 原文链接:Django笔记三十八之发送邮件 这一篇笔记介绍如何在 Django 中发送邮件. 在 Python 中,提供了 smtplib 的邮件模块,而 Djan ...
- 回溯理论基础及leetcode例题
学习参考 回溯 与递归相辅相成;回溯是递归的副产品,只要有递归就会有回溯. 回溯函数也就是递归函数,指的都是一个函数. 回溯搜索法 纯暴力搜索 解决的问题 组合问题:N个数里面按一定规则找出k个数的集 ...
- jdg安装及环境设置
1.下载java8 java11 到默认路径 2.右击我的电脑,点击属性 3.点击高级系统设置 4.点击环境变量 5.点击新建 6. 按照提示新建条目 第一个 变量名填入: JAVA_HOME 变量值 ...
- 【重学C++】04 | 说透C++右值引用(上)
文章首发 [重学C++]04 | 说透C++右值引用(上) 引言 大家好,我是只讲技术干货的会玩code,今天是[重学C++]的第四讲,在前面<03 | 手撸C++智能指针实战教程>中,我 ...
- odoo开发教程十一:视图综述
一:视图标签等公共结构 name (必选) 用于通过名字查找标签 model: 与view相关联的model priority 当搜索查找view时,优先级最低的view会被返回 arch 视图lay ...
- [转载]C++ 入门教程(41课时) - 阿里云大学
C++ 教程 C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的.C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言.C++ ...
- To ChatGPT:让你更加随意地使用所有ChatGPT应用
现在其实已经有很多在线的llm服务了,当然也存在许多开源部署方案,但是不知道大家有没有发现一个问题,目前基于ChatGPT开发的应用,都是使用的OpenAI的接口.换句话说,如果没有OpenAI账号, ...
- JavaWeb中Servlet、web应用和web站点的路径细节("/"究竟代表着什么)
JavaWeb中Servlet.web应用和web站点的路径细节("/"究竟代表着什么) 1 开门见山 新建一个tomcat web项目,配置tomcat的虚拟目录,取默认值(/项 ...
- 前端仿京东、天猫带搜索历史搜索栏搜索框searchBar
快速实现前端仿京东.天猫带搜索历史搜索栏搜索框searchBar, 使用简单便利, 请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=12529 ...