欢送拜访我的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");    }}
  • 上述代码有以下几点须要留神
  1. 用注解<font color="blue">Interceptor</font>和<font color="blue">TrackLifeCycle</font>润饰,阐明这是拦截器TrackLifeCycle的实现
  2. 被拦挡bean实例化的时候,<font color="blue">AroundConstruct</font>润饰的办法execute就会被执行,这和[《拦截器》]()一文中的<font color="red">AroundInvoke</font>的用法很类似
  3. 被拦挡bean创立胜利后,<font color="blue">PostConstruct</font>润饰的办法doPostConstruct就会被执行
  4. 被拦挡bean在销毁之前,<font color="blue">PreDestroy</font>润饰的办法doPreDestroy就会被执行
  • 接下来是应用拦截器<font color="blue">TrackLifeCycle</font>了,用于演示的bean如下,用TrackLifeCycle润饰,有构造方法和简略的helloWorld办法
@ApplicationScoped@TrackLifeCyclepublic class Hello {    public Hello() {        Log.info(this.getClass().getSimpleName() + " at instance");    }    public void helloWorld() {        Log.info("Hello world!");    }}
  • 最初再写个单元测试类验证
@QuarkusTestpublic 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:808115: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 instance15:26:33,040 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) start AroundConstruct15:26:33,040 INFO  [com.bol.lif.Hello] (main) Hello_Subclass at instance15:26:33,040 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) end AroundConstruct15:26:33,041 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PostConstruct15:26:33,041 INFO  [com.bol.lif.Hello] (main) Hello world!15:26:33,097 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PreDestroy15: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也反对上述形式,不过和拦截器相比有两个差别:
  1. 在bean的外部,只能用PostConstruct和TrackLifeCycle,不能用AroundConstruct,只有拦截器能力用AroundConstruct
  2. 在拦截器中,PostConstruct和TrackLifeCycle润饰的办法必须要有InvocationContext类型的入参,然而在bean外部则没有此要求
  • 咱们来革新<font color="blue">Hello.java</font>的源码,批改后如下,减少了两个办法,别离被PostConstruct和PreDestroy润饰
@ApplicationScoped@TrackLifeCyclepublic 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:808116: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 instance16:27:54,710 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) start AroundConstruct16:27:54,711 INFO  [com.bol.lif.Hello] (main) Hello_Subclass at instance16:27:54,711 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) end AroundConstruct16:27:54,711 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PostConstruct16:27:54,712 INFO  [com.bol.lif.Hello] (main) at doPostConstruct16:27:54,712 INFO  [com.bol.lif.Hello] (main) Hello world!16:27:54,747 INFO  [com.bol.int.imp.LifeCycleInterceptor] (main) life cycle PreDestroy16:27:54,747 INFO  [com.bol.lif.Hello] (main) at PreDestroy16:27:54,765 INFO  [io.quarkus] (main) Quarkus stopped in 0.044s

dispose注解:实现销毁前自定义操作,dispose是另一种可选计划

  • 试想这样的场景:我的bean在销毁前要做自定义操作,然而如果用之前的两种计划,可能面临以下问题:
  1. 不适宜批改bean的代码,bean的类可能是第三方库
  2. 也不适宜批改生命周期拦截器代码,拦截器可能也是第三方库,也可能是多个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次
@QuarkusTestpublic 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>办法,合乎预期

  • 至此,生命周期回调相干的实战就实现了,心愿能给您一些参考,接下来的文章会持续深刻学习依赖注入相干的知识点

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

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