欢送拜访我的 GitHub
这里分类和汇总了欣宸的全副原创 (含配套源码):https://github.com/zq2599/blog_demos
对于依赖注入
- 对一名 java 程序员来说,依赖注入应该是个相熟的概念,简略的说就是:我要用 XXX,但我不负责 XXX 的生产
- 以下代码来自 spring 官网,serve 办法要应用 MyComponent 类的 doWork 办法,然而不负责 MyComponent 对象的实例化,只有用注解 Autowired 润饰成员变量 myComponent,spring 环境会负责为 myComponent 赋值一个实例
@Service
public class MyService {
@Autowired
MyComponent myComponent;
public String serve() {myComponent.doWork();
return "success";
}
}
- 对于依赖注入,网上有很多优良文章,这里就不开展了,咱们要关注的是 quarkus 框架的依赖注入
对于《quarkus 依赖注入》系列
- 《quarkus 依赖注入》共六篇文章,整体规划上隶属于《quarkus 实战》系列,但专一于依赖注入的知识点和实战
- 如果您相熟 spring 的依赖注入,那么浏览本系列时会发现 quarkus 与 spring 之间有太多相似之处,很多中央一看就懂
本篇概览
- 作为《quarkus 依赖注入》的开篇,本文先介绍 CDI,再学习如何创立 bean 实例,全文内容如下
graph LR
L1(本篇内容) --> 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 标准的内容(请原谅欣宸的英语水平):
- 该标准定义了一组弱小的补充服务,有助于改良利用程序代码的构造
- 给有状态对象定义了生命周期,这些对象会绑定到上下文,上下文是可扩大的
- 简单的、平安的依赖注入机制,还有开发和部署阶段抉择依赖的能力
- 与 Expression Language (EL) 集成
- 装璜注入对象的能力(集体想到了 AOP,你拿到的对象其实是个代理)
- 拦截器与对象关联的能力
- 事件告诉模型
- web 会话上下文
- 一个 SPI:容许 <font color=”blue”> 便携式扩大 </font> 与 <font color=”blue”> 容器 </font> 的集成(integrate cleanly)
对于 CDI 的 bean
- CDI 的实现(如 quarkus),容许对象做这些事件:
- 绑定到生命周期上下文
- 注入
- 与拦截器和装璜器关联
- 通过触发和察看事件,以涣散耦合的形式交互
- 上述场景的对象统称为 <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 注解就能够注入了
@Component
public 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;
@ApplicationScoped
public 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;
@QuarkusTest
class 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;
@QuarkusTest
class 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 之后还有更精彩的内容为您奉上,敬请期待
欢送关注思否:程序员欣宸
学习路上,你不孤独,欣宸原创一路相伴 …