欢送拜访我的 GitHub
这里分类和汇总了欣宸的全副原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本篇的知识点是 bean 的生命周期回调:在 bean 生命周期的不同阶段,都能够触发自定义代码的执行
- 触发自定义代码执行的具体形式,是用对应的注解去润饰要执行的办法,如下图所示:
- 有两种模式能够实现生命周期回调:拦截器模式和自定义模式,接下来通过编码顺次学习
拦截器模式
- 《拦截器(Interceptor)》已具体介绍了 quarkus 拦截器的自定义和应用,包含以下三个步骤
<img src=”https://typora-pictures-1253575040.cos.ap-guangzhou.myqcloud.com/%E6%B5%81%E7%A8%8B%E5%9B%BE%20(19).jpg” alt=” 流程图 (19)” style=”zoom:67%;” />
- 如果要自定义 bean 的生命周期回调,也是遵循上述步骤执行,接下来编码实现
- 首先定义拦截器,名为 <font color=”blue”>TrackLifeCycle</font>,就是个一般拦截器,须要用注解 <font color=”red”>InterceptorBinding</font> 润饰
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");
}
}
- 上述代码有以下几点须要留神
- 用注解 <font color=”blue”>Interceptor</font> 和 <font color=”blue”>TrackLifeCycle</font> 润饰,阐明这是拦截器 TrackLifeCycle 的实现
- 被拦挡 bean 实例化的时候,<font color=”blue”>AroundConstruct</font> 润饰的办法 execute 就会被执行,这和 [《拦截器》]() 一文中的 <font color=”red”>AroundInvoke</font> 的用法很类似
- 被拦挡 bean 创立胜利后,<font color=”blue”>PostConstruct</font> 润饰的办法 doPostConstruct 就会被执行
- 被拦挡 bean 在销毁之前,<font color=”blue”>PreDestroy</font> 润饰的办法 doPreDestroy 就会被执行
- 接下来是应用拦截器 <font color=”blue”>TrackLifeCycle</font> 了,用于演示的 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 外部则没有此要求
- 咱们来革新 <font color=”blue”>Hello.java</font> 的源码,批改后如下,减少了两个办法,别离被 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 和拦截器的代码,用注解 <font color=”blue”>dispose</font> 润饰指定办法即可,接下来编码验证
- 减少一个一般类 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 的生命周期是 <font color=”blue”>RequestScoped</font>,因而每次申请 <font color=”blue”>/resourcemanager</font> 都会实例化一个 ResourceManager,申请完结后再将其销毁
- 当初,业务需要是每个 ResourceManager 的 bean 在销毁前,都要求其 <font color=”red”>closeAll</font> 办法被执行
- <font color=”red”> 重点来了 </font>,在 SelectBeanConfiguration.java 中新增一个办法,入参是 bean,而且要用 <font color=”blue”>Disposes</font> 注解润饰,如此,<font color=”red”>ResourceManager 类型的 bean 在销毁前此办法都会被执行</font>
/**
* 应用了 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,这里用了注解 <font color=”blue”>RepeatedTest</font> 示意反复执行,属性值为 3,示意反复执行 3 次
@QuarkusTest
public class DisposeTest {@RepeatedTest(3)
public void test() {given()
.when().get("/resourcemanager")
.then()
.statusCode(200)
// 查看 body 内容
.body(is("success"));
}
}
- 执行单元测试,控制台输入如下图,可见每次申请都有 bean 创立,也随同着 bean 销毁,每次销毁都会执行 <font color=”blue”>closeResource</font> 办法,合乎预期
- 至此,生命周期回调相干的实战就实现了,心愿能给您一些参考,接下来的文章会持续深刻学习依赖注入相干的知识点
欢送关注思否:程序员欣宸
学习路上,你不孤独,欣宸原创一路相伴 …