1.11.笙歌散:JSR330/JSR340
促织絮寒霜气白,隔墙谁弄紫鸾笙。——朱权《宮词》
项目地址:https://github.com/silentbalanceyh/vertx-zero-example/(子项目:up-rhea)
本文主要讲解Zero中对两个核心JSR的支持情况:
JSR340:Java Servelt 3.1 Specification(只支持Filter功能)
JSR330:Dependency Injection for Java(依赖注入功能)
「壹」JSR340
由于Vert.x的整体架构和传统服务器有所区别,从实战经验上看,支持Servlet/JSP并没有太大必要,现阶段大部分系统都使用了前后端分离,后端更多使用的是RESTful,所以Zero对JSR340的支持中,主要是支持@WebFilter的** 前端过滤器**。
责任链全称是Chain of Responsibility(COR),它是设计模式中的一种,它将一些请求处理的对象串在一个“绳子”上,前一个处理完后,下一个继续处理,直到所有对象都处理完。责任链上的每一个对象处理过程中只向下一个对象传递数据,相互之间各自独立(近似前一章节的插件模式)。
1.1. 代码示例
先看一段基础代码:
Agent代码
package cn.vertxup.filter;
import io.vertx.core.json.JsonObject;
import io.vertx.up.annotations.EndPoint;
import jakarta.ws.rs.BodyParam;
import jakarta.ws.rs.ContextParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
@EndPoint
@Path("/api")
public class FilterAgent {
@POST
@Path("/jsr340/agent")
public JsonObject filter(@BodyParam final JsonObject data,
@ContextParam("contextKey") final String filtered) {
return new JsonObject()
.put("filter", filtered)
.mergeIn(data);
}
}Filter代码
运行上述代码并发送请求:
可得到如下响应:
FirstFilter的执行在FilterAgent之前,它修改了请求数据,并且将数据设置到上下文对象中,而且使用了JSR340中的核心注解javax.servlet.annotation.WebFilter ,对Servlet中Filter熟悉的读者应该对这种代码不陌生。
在读取数据信息过程中,代码使用了
@ContextParam注解从上下文环境中读取Filter填充的数据。
1.2. HttpFilter类
注意上述示例中代码FirstFilter是直接从HttpFilter继承而来,该类的定义如下:
而此处Filter接口的定义如下:
Vert.x的vertx-web 项目中,请求和响应对象和常用JSR340中的ServletRequest以及ServletResponse有所区别,所以Zero不得不创建自己的Filter结构去适配@WebFilter的基础逻辑,并且将常用的ServletException异常改成VertxException异常。虽然二者的请求和响应对象有所不同,但逻辑层面上从代码运行结果可以知道,它们是类似的,Zero容器也将@WebFilter部分的逻辑补充完整了。
HttpFilter中保存了io.vertx.ext.web.RoutingContext引用,该对象是vertx-web 中的重要上下文对象,它已经帮助您绑定好了该对象,您可以使用该对象获取下边引用:
Session会话对象
Cookie对象
Annal日志对象
如此,您的Filter代码就简化很多了;为了让曾经开发过Servlet的Filter开发人员对此部分开发顺利上手,方法名称直接使用了doFilter。
1.3. 链式结构
最后讲解一个JSR340的责任链代码示例:
Agent代码
FirstFilter
直接修改第一个例子中FirstFilter主逻辑,增加打印语句查看执行顺序。
SecondFilter
运行上述代码并发送请求:
可得到如下响应:
后台输出可看到Filter的执行顺序:
目前为止,Zero中关于过滤器部分的内容就讲解完了,简单总结一下:
Zero中的过滤器Filter类使用了JSR340中的
javax.servlet.annotation.WebFilter注解,并且该类必须实现Zero定义的过滤器接口io.vertx.up.uca.web.filter.Filter。推荐使用
io.vertx.up.uca.web.filter.HttpFilter抽象类,可引入上下文环境信息而在过滤器中获取更多vertx-web中的核心对象,如Session、Cookie、Annal等。若要对过滤器Filter排序,可使用Zero中的扩展注解
io.vertx.up.annotations.Ordered,如此编排后,您的过滤器就可以顺利形成一个过滤链。在Agent中若要读取上下文中参数信息可使用
jakarta.ws.rs.ContextParam注解。
「贰」JSR330
这里就不解释依赖注入的详细内容了,读者可以上网查阅,铺天盖地的依赖注入,如今的DI几乎已经成为了一种系统设计标准。
依赖注入全称Dependency Injection(DI),简单说就是组件之间的依赖关系由容器全程控制并且在运行时决定,容器的控制使得这些组件之间不存在过多的依赖关系,从传统调用到依赖注入的转变参考下图:

上图是反转控制的一个基本转变。左边的图中,B对象由A使用new B() 创建,也就是说如果A对象被销毁,B对象会随之销毁,等价于B类依赖A类而存在,控制权在A;右边的图中,B对象由容器使用new B() 创建,而A只是获取了B的引用,这种时候,如果A销毁,B对象本身并不会受到影响,控制权从A转换到容器中,而调用B类的模式并没有发生太大变化。
Zero主要使用三个注解:
JSR330:
javax.inject.InjectJSR330:
javax.inject.NamedZero:
io.vertx.up.annotations.Qualifier
Zero中的依赖注入主要是服务于实战,并非规范,所以它有一定的限制:在Zero中,默认注入实例是单例的,不需使用javax.inject.Singleton 注解,所以只能用它注入非数据对象,如果是数据对象类似Pojo和JavaBean,在Zero中建议不使用,——即使使用了注解,也是单例的。
Zero中的JSR330支持分三种:
简单Java对象
Java接口和单类实现
Java接口和多类实现
2.1. 简单Java对象
在简单Java对象注入过程中,注入的Java对象使用class定义。
Java对象代码
Agent代码段
Worker代码
Zero中定义注入点的代码如下:
javax.inject.Inject 是JSR330中的核心注解,有了该注解,就可以直接对某些非数据对象执行注入,而SimpleObject使用的就是简单Java对象(class定义)。发送请求后您将得到如下响应信息:
2.2. Java接口和单类实现
这种类型中,使用了两个Java文件定义被注入对象(interface定义接口,class定义实现)。
Java接口代码
Java类代码
Agent代码段
Worker代码
发送请求到对应接口:
您可以得到如下响应:
从实战中可以知道,这种模式是在Zero项目开发中使用最高频的一种方式,在复杂的企业系统中,很少定义一个单独的class来做逻辑实现而是沿用面向接口 的设计,之所以使用单个实现类是因为Worker组件中使用的组件往往是曾经可直接生成的业务逻辑层Service的逻辑代码(如果使用生成框架,几乎都是一对一),于是本小节的情况就频繁使用了。
Zero中的基础编程规范如:
XxxStub
interface
定义业务逻辑层接口,曾经的XxxService
XxxService
class
定义业务逻辑层实现类,曾经的XxxServiceImpl
思考 :注入的使用在系统里最终会让系统内部组件和组件之间耦合性很低,但是反过来思考,如果您正在开发一个内聚性很高的组合组件,那么是不是所有调用和联结点都一定要使用注入呢?在我看来不尽然,在合适的地方使用注入,在某些地方将两个组件结合到一起也是一种选择,归根到底,软件设计是自由的,不能太遵循约定盲目而为之。
2.3. Java接口和多类实现
最后一种情况是一个接口和多个实现类,在多个实现类中,由于系统无法根据接口注入找到唯一实现类,所以这种情况下还依赖另外两个注解。
JSR330:
javax.inject.NamedZero:
io.vertx.up.annotations.Qualifier
看看如下代码示例:
Java接口代码
实现类A
实现类B
Agent代码段
Worker代码
上述代码最终测试响应如:
「叁」小结
本章节主要讲解了Zero对JSR340和JSR330的支持,Zero中对二者的支持不是完整的 ,但现有的功能已经足够使用,后续版本中会根据实际项目需求逐渐开放其他功能,了解了本文中的内容后,您就可以直接在Zero中使用这两种规范了。
Zero在注解的添加和设计过程中,尽可能少地引入自定义注解,其目的是让开发人员不用学习太多新注解,如果您对JSR311,JSR303,JSR340,JSR330 足够熟悉的话,那么相关功能在Zero中是可以直接使用的,这样就使得读者上手Zero更容易。
Last updated