03-CDI-Quarkus的DI解决方案

Contexts and Dependency Injection (CDI)

简单来讲就是Quarkus 基于 Java JSR CDI标准的实现。类似于Spring中的Bean的管理,如果事先用过Quarkus的话就会发现,在使用Quarkus中总有点Spring的味道。今天先看一下Quarkus中CDI的基本功能。

本章目标

  • 1.Quarkus中的Bean是什么样子的。
  • 2.如果有多个相同类型的bean如何处理。
  • 3.Bean的注入方法有哪些。
  • 4.什么是qualifiers。
  • 5.bean scope 有哪些。
  • 6.client proxies是什么?
  • 7.有哪些Bean类型。
  • 8.生命周期回调。
  • 9.事件回调。
  • 10.拦截器。
1.Quarkus中的Bean是什么样子的
import javax.inject.Inject;
import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.metrics.annotation.Counted;

@ApplicationScopedpublic class Translator {

    @Inject ②
    Dictionary dictionary; 
}

上面代码中Translator就是Quarkus中的一个最基本的Bean,可以看到除了使用的注解不同之外,是不是很像Spring中的Bean的用法。所以我们在学习quarkus时多对比一下Spring。
① @ApplicationScoped 表示这个类是一个被管理的Bean,不过是延迟加载的。
② @Inject 表示注入一个Bean,也就是Translator依赖Dictionary。
无论Spring还是Quarkus中Bean的定义和注入方式都是类似的,只是实现方法还需要我们以后深入探究。

2.如果有多个相同类型的bean如何处理。

我们清楚在Spring中有多个ID或者类名相同的Bean可以在注入的时候指定Bean名称,来明确要注入的Bean,那Quarkus中是如何处理的呢?
a bean is assignable to an injection point if the bean has a bean type that matches the required type and has all the required qualifiers 官方文档是这样说的,这里有两点 :一是根据类型匹配注入点的;二是满足所有的注入点限定符。
那如果是一个注入点有多个满足条件的Bean的话怎么处理呢?

  1. 快速失败,在构建时就会抛出AmbiguousResolutionException异常.
    ServiceA、ServiceB实现了Service接口,我们在注入Service时,在项目启动时就会报错。
    在这里插入图片描述
  2. 注入多个,使用Instance实例包裹所有符合条件的Bean。= = 简单粗暴,但不优雅。
public class Translator {

    @Inject
    Instance<Service> services;

    @GET
    @Path("/single")
    public String hello(){
        services.forEach(service -> {
            System.out.println(service.hello());
        });
        return "service.hello()";
    }
}
3.Bean的注入方法有哪些
  1. @Inject注入
  2. Setter方法注入
  3. 构造器注入

非常简单也和Spring类似,这里只讲一下set方法注入,方法必须用@Inject修饰,同时不需要定义Bean变量,每一个参数都是一个注入点,也就是说可以在一个set方法注入多个Bean。

4.什么是qualifiers

qualifiers是用来标识一个Bean的,比如在注入时,需要满足qualifiers条件的才能注入。也就是一个bean的标识。它是一个注解,我们可以自定义一个注解来修饰bean。

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Superior {}
@Superior
@ApplicationScoped
public class ServiceB implements Service{
    @Override
    public String hello() {
        return "ServiceB";
    }
}

我们在ServiceB上添加了自定义的Qualifiers注解,项目正常启动并且可以正常执行,说明加了注解之后ServiceB就不在满足注入时的条件了。

5.bean scope 有哪些
AnnotationDescription
@javax.enterprise.context.ApplicationScoped一个单例Bean,同时是懒加载的,只有被调用时才会初始化
@javax.inject.Singleton类似ApplicationScoped只是没有client proxy
@javax.enterprise.context.RequestScoped每一次Http Request 创建一个bean
@javax.enterprise.context.Dependent这个一个伪作用域,跟随注入它的bean的生命周期
@javax.enterprise.context.SessionScoped跟随HttpSession生命周期,只在quarkus-undertow下有效
6.client proxies是什么

简单来说,它是一个代理对象,对bean 方法的调用都是通过它来调用的。
实际注入一个Bean时也是注入的代理对象。
代理对象
暂时先简单了解这个,自己还没整明白,不敢乱写。。。
代理对象的特点:

  1. 懒加载,在代理对象被调用时创建实例。
  2. 允许注入一个比当前作用域更小的作用域。
  3. 允许循环依赖,思考Spring中也只允许了单例的循环依赖
  4. In rare cases it’s practical to destroy the beans manually. A direct injected reference would lead to a stale bean instance. 还没搞懂。
7.有哪些Bean类型
  1. Class beans 我们上面讲的Bean都是这个类型的
  2. Producer methods 通过方法产生的Bean
  3. Producer fields 通过属性产生的Bean
  4. Synthetic beans 合成Bean一般是第三方工具使用的

这里讲下 2 和 3:

@ApplicationScoped
public class ProduceBean {

    @Produces
    double PI = 3.1415;@Produces
    public List<String> names(){return Arrays.asList("jack", "rose");
    }

}
@Path("produces")
public class ProducesResource {

    @Inject
    double PI;

    @Inject
    List<String> names;

    @GET
    public String test(){

        System.out.println(PI);

        names.forEach(System.out::println);

        return "SUCCESS";
    }
}

① 这个浮点数将被注册为Bean的元数据,可以被直接注入使用,由于没有定义这个属性的作用域,所以默认为@Dependent,将跟随注入它的bean的生命周期。 我们也可以添加生命周期限定。
②类似①只是注册的是方法的返回值类型。

8.生命周期回调
@ApplicationScoped
public class Translator {

    @PostConstruct 
    void init() {
       // ...
    }

    @PreDestroy 
    void destroy() {
      // ...
    }
}
9.事件回调
class TaskCompleted {
  // ...
}

@ApplicationScoped
class ComplicatedService {

   @Inject
   Event<TaskCompleted> event;void doSomething() {
      // ...
      event.fire(new TaskCompleted());}

}

@ApplicationScoped
class Logger {

   void onTaskCompleted(@Observes TaskCompleted task) {// ...log the task
   }

}

① 用来触发事件的
② 添加触发事件
③ @Observes 监听事件触发

10.拦截器

定义一个拦截器注解

@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
@InterceptorBinding
public @interface MyInterceptor {}

实现拦截器逻辑

@com.quarkus.annotation.MyInterceptor
@Interceptor
public class MyInterceptor {

    @AroundInvoke
    public Object around(InvocationContext context) throws Exception {

        System.out.println("before");
		// 继续下一个拦截器或者运行实际逻辑
        Object res = context.proceed();

        System.out.println("after");

        return res;
    }
}

使用拦截器注解指定要拦截的方法

    @MyInterceptor
    @GET
    public String test(){

        System.out.println(PI);

        names.forEach(System.out::println);

        return "SUCCESS";
    }

结果:

before
3.1415
jack
rose
after

总结

本章只是简单介绍了Quarkus的CDI简单使用方式,和基本概念。后面会一篇更深入的文章。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值