欢送拜访我的GitHub

这里分类和汇总了欣宸的全副原创(含配套源码):https://github.com/zq2599/blog_demos

对于依赖注入

  • 对一名java程序员来说,依赖注入应该是个相熟的概念,简略的说就是:我要用XXX,但我不负责XXX的生产
  • 以下代码来自spring官网,serve办法要应用MyComponent类的doWork办法,然而不负责MyComponent对象的实例化,只有用注解Autowired润饰成员变量myComponent,spring环境会负责为myComponent赋值一个实例
@Servicepublic class MyService {        @Autowired    MyComponent myComponent;        public String serve() {        myComponent.doWork();        return "success";    }}
  • 对于依赖注入,网上有很多优良文章,这里就不开展了,咱们要关注的是quarkus框架的依赖注入

对于《quarkus依赖注入》系列

  • 《quarkus依赖注入》共六篇文章,整体规划上隶属于《quarkus实战》系列,但专一于依赖注入的知识点和实战
  • 如果您相熟spring的依赖注入,那么浏览本系列时会发现quarkus与spring之间有太多相似之处,很多中央一看就懂

本篇概览

  • 作为《quarkus依赖注入》的开篇,本文先介绍CDI,再学习如何创立bean实例,全文内容如下
graph LRL1(本篇内容) --> L2-1(官网揭示)L1 --> L2-2(CDI)L1 --> L2-3(创立bean)L2-2 --> L3-1(对于CDI)L2-2 --> L3-2(对于bean)L2-3 --> L3-3(注解润饰在类上)L2-3 --> L3-4(注解润饰在办法上)L2-3 --> L3-5(注解润饰在成员变量上)L2-3 --> L3-6(扩大组件中的synthetic bean)
  • 学习quarkus的依赖注入之前,来自官网的揭示十分重要

官网揭示

  • 在应用依赖注入的时候,quankus官网倡议<font color="red">不要应用公有变量</font>(用默认可见性,即雷同package内可见),因为GraalVM将利用制作成二进制可执行文件时,编译器名为<font color="blue">Substrate VM</font>,操作公有变量须要用到反射,而GraalVM应用反射的限度,导致动态编译的文件体积增大
Quarkus is designed with Substrate VM in mind. For this reason, we encourage you to use *package-private* scope instead of *private*.

对于CDI

  • 《 Contexts and Dependency Injection for Java 2.0》,简称CDI,该标准是对JSR-346的更新,quarkus对依赖注入的反对就是基于此标准实现的
  • 从 2.0 版开始,CDI 面向 Java SE 和 Jakarta EE 平台,Java SE 中的 CDI 和 Jakarta EE 容器中的 CDI 共享core CDI 中定义的个性。
  • 简略看下CDI标准的内容(请原谅欣宸的英语水平):
  1. 该标准定义了一组弱小的补充服务,有助于改良利用程序代码的构造
  2. 给有状态对象定义了生命周期,这些对象会绑定到上下文,上下文是可扩大的
  3. 简单的、平安的依赖注入机制,还有开发和部署阶段抉择依赖的能力
  4. 与Expression Language (EL)集成
  5. 装璜注入对象的能力(集体想到了AOP,你拿到的对象其实是个代理)
  6. 拦截器与对象关联的能力
  7. 事件告诉模型
  8. web会话上下文
  9. 一个SPI:容许<font color="blue">便携式扩大</font>与<font color="blue">容器</font>的集成(integrate cleanly )

对于CDI的bean

  • CDI的实现(如quarkus),容许对象做这些事件:
  1. 绑定到生命周期上下文
  2. 注入
  3. 与拦截器和装璜器关联
  4. 通过触发和察看事件,以涣散耦合的形式交互
  • 上述场景的对象统称为<font color="red">bean</font>,上下文中的 bean 实例称为<font color="blue">上下文实例</font>,上下文实例能够通过依赖注入服务注入到其余对象中
  • 对于CDI的背景常识就介绍到这里吧,接下来要写代码了

源码下载

  • 本篇实战的残缺源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
名称链接备注
我的项目主页https://github.com/zq2599/blog_demos该我的项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blog_demos.git该我的项目源码的仓库地址,https协定
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该我的项目源码的仓库地址,ssh协定
  • 这个git我的项目中有多个文件夹,本次实战的源码在<font color="blue">quarkus-tutorials</font>文件夹下,如下图红框
    <img src="https://typora-pictures-1253575040.cos.ap-guangzhou.myqcloud.com/image-20220312091203116.png" alt="image-20220312091203116" style="zoom: 80%;" />
  • <font color="blue">quarkus-tutorials</font>是个父工程,外面有多个module,本篇实战的module是<font color="red">basic-di</font>,如下图红框
    <img src="https://typora-pictures-1253575040.cos.ap-guangzhou.myqcloud.com/image-20220312091404031.png" alt="image-20220312091404031" style="zoom:80%;" />

创立demo工程

  • 您能够参考《quarkus实战之二:利用的创立、构建、部署》,创立个最简略的web工程,默认生成一个web服务类HobbyResource.java,代码如下,前面的演示代码都写在这个工程中
package com.bolingcavalry;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;import java.time.LocalDateTime;@Path("/actions")public class HobbyResource {    @GET    @Produces(MediaType.TEXT_PLAIN)    public String hello() {        return "Hello RESTEasy, " + LocalDateTime.now();    }}
  • 接下来,从最根底的创立bean实例创立开始

创立bean实例:注解润饰在类上

  • 先来看看spring是如何创立bean实例的,回顾文章刚开始的那段代码,myComponent对象来自哪里?
  • 持续看spring官网的demo,如下所示,用<font color="blue">Component</font>注解润饰在类上,spring就会实例化MyComponent对象并注册在bean容器中,须要用此bean的时候用Autowired注解就能够注入了
@Componentpublic class MyComponent {    public void doWork() {}}
  • quarkus框架下也有相似形式,演示类ClassAnnotationBean.java如下,用注解<font color="blue">ApplicationScoped</font>去润饰ClassAnnotationBean.类,如此quarkus就会实例化此类并放入容器中
package com.bolingcavalry.service.impl;import javax.enterprise.context.ApplicationScoped;@ApplicationScopedpublic class ClassAnnotationBean {    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 这种注解润饰在类上的bean,被quarkus官网成为<font color="blue">class-based beans</font>
  • 应用bean也很简略,如下,用注解<font color="blue">Inject</font>润饰ClassAnnotationBean类型的成员变量即可
package com.bolingcavalry;import com.bolingcavalry.service.impl.ClassAnnotationBean;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;import java.time.LocalDateTime;@Path("/classannotataionbean")public class ClassAnnotationController {    @Inject    ClassAnnotationBean classAnnotationBean;    @GET    @Produces(MediaType.TEXT_PLAIN)    public String hello() {        return String.format("Hello RESTEasy, %s, %s",                LocalDateTime.now(),                classAnnotationBean.hello());    }}
  • 如何验证上述代码是否无效?运行服务,再用浏览器拜访<font color="blue">classannotataionbean</font>接口,肉眼判断返回内容是否符合要求,这样尽管可行,但总感觉会被讥嘲低效...
  • 还是写一段单元测试代码吧,如下所示,留神要用<font color="blue">QuarkusTest</font>注解润饰测试类(不然服务启动有问题),测试方法中查看了返回码和body,如果后面的依赖注入没问题,则上面的测试应该能通过才对
package com.bolingcavalry;import com.bolingcavalry.service.impl.ClassAnnotationBean;import io.quarkus.test.junit.QuarkusTest;import org.junit.jupiter.api.Test;import static io.restassured.RestAssured.given;import static org.hamcrest.CoreMatchers.containsString;@QuarkusTestclass ClassAnnotationControllerTest {    @Test    public void testGetEndpoint() {        given()                .when().get("/classannotataionbean")                .then()                .statusCode(200)                // 查看body内容,是否含有ClassAnnotationBean.hello办法返回的字符串                .body(containsString("from " + ClassAnnotationBean.class.getSimpleName()));    }}
  • 执行命令<font color="blue">mvn clean test -U</font>开始测试,控制台输入如下,提醒测试通过
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0[INFO] [INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time:  5.702 s[INFO] Finished at: 2022-03-12T15:48:45+08:00[INFO] ------------------------------------------------------------------------
  • 如果您的开发工具是IDEA,也能够用它的图形化工具执行测试,如下图,能失去更丰盛的测试信息

  • 把握了最根底的实例化形式,接着看下一种形式:润饰在办法上

创立bean实例:注解润饰在办法上

  • 下一种创立bean的形式,咱们还是先看spring是怎么做的,有了它作比照,对quarkus的做法就好了解了
  • 来看spring官网文档上的一段代码,如下所示,用<font color="blue">Bean</font>注解润饰<font color="red">myBean</font>办法,spring框架就会执行此办法,将返回值作为bean注册到容器中,spring把这种bean的处理过程称为<font color="blue">lite mode</font>
@Component public class Calculator {     public int sum(int a, int b) {         return a+b;     }     @Bean     public MyBean myBean() {         return new MyBean();     } }
  • kuarkus框架下,也能用注解润饰办法来创立bean,为了演示,先定义个一般接口
package com.bolingcavalry.service;public interface HelloService {    String hello();}
  • 以及HelloService接口的实现类
package com.bolingcavalry.service.impl;import com.bolingcavalry.service.HelloService;public class HelloServiceImpl implements HelloService {    @Override    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 留神,HelloService.java和HelloServiceImpl.java都是一般的java接口和类,与quarkus没有任何关系
  • 上面的代码演示了用注解润饰办法,使得quarkus调用此办法,将返回值作为bean实例注册到容器中,<font color="blue">Produces</font>告诉quarkus做实例化,<font color="red">ApplicationScoped</font>表明了bean的作用域是整个利用
package com.bolingcavalry.service.impl;import com.bolingcavalry.service.HelloService;import javax.enterprise.context.ApplicationScoped;import javax.enterprise.inject.Produces;public class MethodAnnonationBean {    @Produces    @ApplicationScoped    public HelloService getHelloService() {        return new HelloServiceImpl();    }}
  • 这种用于创立bean的办法,被quarkus称为<font color="blue">producer method</font>
  • 看过上述代码,置信聪慧的您应该明确了用这种形式创立bean的长处:在创立HelloService接口的实例时,能够管制所有细节(构造方法的参数、或者从多个HelloService实现类中抉择一个),没错,在SpringBoot的Configuration类中咱们也是这样做的
  • 后面的getHelloService办法的返回值,能够间接在业务代码中依赖注入,如下所示
package com.bolingcavalry;import com.bolingcavalry.service.HelloService;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;import java.time.LocalDateTime;@Path("/methodannotataionbean")public class MethodAnnotationController {    @Inject    HelloService helloService;    @GET    @Produces(MediaType.TEXT_PLAIN)    public String get() {        return String.format("Hello RESTEasy, %s, %s",                LocalDateTime.now(),                helloService.hello());    }}
  • 单元测试代码如下
package com.bolingcavalry;import com.bolingcavalry.service.impl.HelloServiceImpl;import io.quarkus.test.junit.QuarkusTest;import org.junit.jupiter.api.Test;import static io.restassured.RestAssured.given;import static org.hamcrest.CoreMatchers.containsString;@QuarkusTestclass MethodAnnotationControllerTest {    @Test    public void testGetEndpoint() {        given()                .when().get("/methodannotataionbean")                .then()                .statusCode(200)                // 查看body内容,HelloServiceImpl.hello办法返回的字符串                .body(containsString("from " + HelloServiceImpl.class.getSimpleName()));    }}
  • 测试通过

  • producer method有个个性须要重点关注:如果方才生产bean的<font color="blue">getHelloService</font>办法有个入参,如下所示,入参是OtherService对象,那么,<font color="red">这个OtherService对象也必须是个bean实例</font>(这就像你用@Inject注入一个bean的时候,这个bean必须存在一样),如果OtherService不是个bean,那么利用初始化的时候会报错,(其实这个个性SpringBoot中也有,置信经验丰富的您在应用Configuration类的时候应该用到过)
public class MethodAnnonationBean {    @Produces    @ApplicationScoped    public HelloService getHelloService(OtherService otherService) {        return new HelloServiceImpl();    }}
  • quarkus还做了个简化:如果有了<font color="blue">ApplicationScoped</font>这样的作用域注解,那么<font color="red">Produces</font>能够省略掉,写成上面这样也是失常运行的
public class MethodAnnonationBean {    @ApplicationScoped    public HelloService getHelloService() {        return new HelloServiceImpl();    }}

创立bean实例:注解润饰在成员变量上

  • 再来看看最初一种形式,注解在成员变量上,这个成员变量就成了bean
  • 先写个一般类用于稍后测试
package com.bolingcavalry.service.impl;import com.bolingcavalry.service.HelloService;public class OtherServiceImpl {    public String hello() {        return "from " + this.getClass().getSimpleName();    }}
  • 通过成员变量创立bean的形式如下所示,给otherServiceImpl减少两个注解,<font color="blue">Produces</font>告诉quarkus做实例化,<font color="red">ApplicationScoped</font>表明了bean的作用域是整个利用,最终OtherServiceImpl实例会被创立后注册到bean容器中
package com.bolingcavalry.service.impl;import javax.enterprise.context.ApplicationScoped;import javax.enterprise.inject.Produces;public class FieldAnnonationBean {    @Produces    @ApplicationScoped    OtherServiceImpl otherServiceImpl = new OtherServiceImpl();}
  • 这种用于创立bean的成员变量(如下面的otherServiceImpl),被quarkus称为<font color="blue">producer field</font>
  • 上述bean的应用办法如下,可见与后面的应用并无区别,都是从quarkus的依赖注入
@Path("/fieldannotataionbean")public class FieldAnnotationController {    @Inject    OtherServiceImpl otherServiceImpl;    @GET    @Produces(MediaType.TEXT_PLAIN)    public String get() {        return String.format("Hello RESTEasy, %s, %s",                LocalDateTime.now(),                otherServiceImpl.hello());    }}
  • 测试代码与后面相似就不赘述了,请您自行实现编写和测试

对于synthetic bean

  • 还有一种bean,quarkus官网称之为<font color="blue">synthetic bean</font>(合成bean),这种bean只会在扩大组件中用到,而咱们日常的利用开发不会波及,synthetic bean的特点是其属性值并不来自它的类、办法、成员变量的解决,而是由扩大组件指定的,在注册syntheitc bean到quarkus容器时,罕用SyntheticBeanBuildItem类去做相干操作,来看一段实例化synthetic bean的代码
@BuildStep@Record(STATIC_INIT)SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {   return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class)                .runtimeValue(recorder.createFoo("parameters are recorder in the bytecode"))                 .done();}
  • 至此,《quarkus依赖注入》的开篇曾经实现,创立bean之后还有更精彩的内容为您奉上,敬请期待

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

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