Dependency Injection - Guice VS Dagger 2

做 Java 后台开发的同学,基本上都使用过 Dependency Injection 依赖注入框架。大名鼎鼎的 Spring Framework 就是从依赖注入起家的,然后一路奔向了全家桶的不归路;Guice 是 Google 公司的依赖注入解决方案,重视类型安全胜过于使用便利性,相比 Spring Framework 能提供 更精准的控制和更翔实的注入失败错误信息;而由 Google 接盘 SquareDagger 2 则比 Guice 更进一步,通过在编译时构建依赖注入关系,使得依赖错误在编译阶 段尽量暴露出来,同时,原生类型安全的 Java 代码相较基于反射的注入代码也有或多或少的性能提升(尤其是在 Android 上)。

本文通过同时使用 GuiceDagger 2 来实现相同的功能,对比二者在实现依赖注入方法上 的异同。

JOOQ 的使用 - 从生成代码执行 SQL 命令 (SQL Executor)

jOOQ 是基于 JDBC 之上的一个抽象层,提供了多种多样的模型来与关系型数据库进行互操作;其使用与 mybatisHibernate ORM 不同的思路来实现 对象关系映射 ORM

JOOQ 的使用 - 代码生成配置 (PostgreSQL & DDL Driven) 介绍了使用 jOOQ 为数据库表生成实体类代码;JOOQ 的使用 - 执行 SQL 语句 (SQL Executor) 介绍了基于 jOOQ 的 SQL 命令执行;作为对比,本篇主要介绍基于生成代码的 SQL 命令执行 (SQL Executor) 。

JOOQ 的使用 - 从生成代码拼接 SQL 语句 (SQL Builder)

jOOQ 是基于 JDBC 之上的一个抽象层,提供了多种多样的模型来与关系型数据库进行互操作;其使用与 mybatisHibernate ORM 不同的思路来实现 对象关系映射 ORM

JOOQ 的使用 - 代码生成配置 (PostgreSQL & DDL Driven) 介绍了使用 jOOQ 为数据库表生成实体类代码;JOOQ 的使用 - 拼接 SQL 语句 (SQL Builder) 介绍了基于 jOOQ 的 SQL 语句拼接;作为对比,本篇主要介绍基于生成代码的 SQL 语句拼接 (SQL Builder) 。

JOOQ 的使用 - 代码生成配置 (PostgreSQL & DDL Driven)

jOOQ 是基于 JDBC 之上的一个抽象层,提供了多种多样的模型来与关系型数据库进行互操作;其使用与 mybatisHibernate ORM 不同的思路来实现 对象关系映射 ORM

jOOQ 的一个最主要的特性就是基于代码生成的类型安全 SQL。其主要是从已有的数据库配置信息中收集 到足够的表结构、字段结构和索引等信息,生成对应的 Java 类型;业务使用生成的 Java 类型来进行 SQL 操作 ,可以得到足够的类型安全保证。同时,由于代码生成避免了基于反射的对象关系映射,在调试时会有更好的表现 。

截止到版本 3.12.1, jOOQ 支持从以下数据库配置源生成代码:

  1. 关系数据库实例,包括(注:免费版本的 jOOQ 可能不支持特定的数据库提供商的产品):
    • Aurora MySQL Edition
    • Aurora PostgreSQL Edition
    • Azure SQL Data Warehouse
    • Azure SQL Database
    • CUBRID
    • DB2 LUW
    • Derby
    • Firebird
    • H2
    • HANA
    • HSQLDB
    • Informix
    • Ingres
    • MariaDB
    • Microsoft Access
    • MySQL
    • Oracle
    • PostgreSQL
    • Redshift
    • SQL Server
    • SQLite
    • Sybase Adaptive Server Enterprise
    • Sybase SQL Anywhere
    • Teradata
    • Vertica
  2. Java Persistence API (JPA) 相关的实体类型(带注解)
  3. 符合约定条件的描述表结构信息等的 XML 文件
  4. 符合标准的用于创建表结构等的 SQL DDL 语句文件

本篇主要介绍基于 jOOQ 的数据库实例和 SQL DDL 文件驱动的代码生成实践。

Mapper Classes Generated by MapStruct

对象实体转换操作在分层应用中很常见,比如数据库层的对象实体和表现层的实体很可能具有不同的属性集,需要在互操作时进行属性的映射(或拷贝)。

MapStruct 是一个辅助进行 Java 实体类之间相互转换的类库,与其他具有相似功能的工具库之间的最大区别在于其使用了 Java 注解处理器 APT 来实现实体间属性的映射而不是使用反射技术。

在 Maven 中支持 Java 的注解处理器 APT

Java 中有很多基于 注解处理器 (Annotation Processing Tool, APT) 技术的类库,如 AutoValueFreeBuilder 等。

Maven 中支持 APT ,需要在 Apache Maven Compiler Plugin 的配置部分添加 annotationProcessorPaths 的配置,如下:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>3.6.1</version>
	<configuration>
		<source>1.8</source>
		<target>1.8</target>
		<testSource>1.8</testSource>
		<testTarget>1.8</testTarget>
		<encoding>UTF-8</encoding>
		<optimize>true</optimize>
		<!-- Slightly faster builds, see https://issues.apache.org/jira/browse/MCOMPILER-209 -->
		<useIncrementalCompilation>false</useIncrementalCompilation>
		<annotationProcessorPaths>
			<path>
				<groupId>com.google.auto.value</groupId>
				<artifactId>auto-value</artifactId>
				<version>${auto-value.version}</version>
			</path>
		</annotationProcessorPaths>
	</configuration>
</plugin>

上述配置对于 Maven 3.5 以上版本有效。

对于低于 3.5 的版本,可以在 dependencies 块中添加依赖项,并设置 optional 属性。

<dependency>
	<groupId>org.inferred</groupId>
	<artifactId>freebuilder</artifactId>
	<version>${freebuilder_version}</version>
	<optional>true</optional>
</dependency>

如果是可执行的工程,也可以设置 scopeprovided

<dependency>
	<groupId>org.inferred</groupId>
	<artifactId>freebuilder</artifactId>
	<version>${freebuilder_version}</version>
	<scope>provided</scope>
</dependency>

Value Class Generated by FreeBuilder

FreeBuilder 是一个非常好用的基于 JAVA 注解处理器 APT (Annotation Processing Tool) 技术的数据实体类生成器,可以 通过简单的注解来生成 Builder 模式的实体类。AutoValue 是另一个非常好用的基于 APT 的类库,参见 Value Class Generated by Google AutoValue。二者的区别在于:

  1. AutoValue 根据开发者提供的一个 Builder 接口来实现具体的 Builder 类;FreeBuilder 则对给定的数据实体类生成符合固定规则的 Builder 类。前者可以控制生成的 Builder 类的方法列表,后者无法控制生成的 Builder 类的方法列表,但是可以通过继承生成的 Builder 类限制对外暴露的方法列表。
  2. AutoValue 需要数据实体类是一个抽象类;FreeBuilder 则同时支持抽象类和接口。

详细对比可以参考 FreeBuilder Alternatives

Value Class Generated by Google AutoValue

Java 中组织数据对象的方式中,最简单也是最通用的是所谓的 POJO (Plain Ordinary Java Object) 即 Java 简单对象。POJO 大法好,好就好在简单实用:只需用遵循简单的命名约定和构造规则,就可以满足数据实体 的需求,而且大多数的序列化方案和持久化方案都能提供 POJO 的支持;如果追求代码精简的话,使用 POJO 可以 和反射方案一起极大地简化重复代码。

POJO 的好处吾等皆非常清楚,然而本文却在讲 AutoValue,因为:

  1. 人是善变的。吾等吃够了鸡蛋牛奶,也会想要尝尝豆浆油条。
  2. equals toString hash 。此三种样板代码,即使是有 IDE 的协助,维护起来仍会非常痛苦。
  3. Mutable shared state is the root of all evil in concurrent systems 。可变性在并发系统中的共享是所有 BUG 的源头,不可变性是救赎之道;而 POJO 不能满足不可变性约束。
  4. Google 大法好。AutoValue 是 Google Java 团队的作品。

AutoValue 是一个基于 Java 注解处理器 APT (Annotation Processing Tool) 的代码生成库,网络上关于 AutoValue 的介绍不要太少,毋庸再讲;其理念和实际用法可见 官网文档,十分钟入门,老少咸 宜。

本文对于 AutoValue 的用法做一个简单汇总,并引申介绍一下基于 AutoValue 的对 象 JSON 序列化方案。

Spring Cloud Gateway Load Testing using Wrk

Spring Cloud GatewaySpring 官方为了完善 Spring Cloud 的版图 而推出的网关服务组件,使用了非阻塞式网络模式和目前流行的响应式编程模型,吸引了很多公司和开发者的注意 力。

网络上有一些现成的对 Spring Cloud Gateway 的性能测试的案例,比如 纠错帖:Zuul & Spring Cloud Gateway & Linkerd性能对比。根据 Simple benchmark comparing zuul and spring cloud gateway 的数据,Spring Cloud Gateway 的性能测试结果参考如下:

ProxyAvg LatencyAvg Req/Sec/Thread
gateway6.61ms3.24k
linkered7.62ms2.82k
zuul12.56ms2.09k
none2.09ms11.77k

但是,由于:

  1. 上述测试只是简单的对后端反向代理测试,而我们知道 Spring Cloud Gateway 对于路由的匹配是顺序的,匹配链后面的路由的性能并没有被关注到。
  2. 测试数据被进行了简化,测试的细节被(有意地)忽略了。

所以,在根据 Spring Cloud Gateway 改造出了初版的公司网关系统之后,我使用 wrkwrk2 对网关进行了多轮的压测,测试结果汇总为本文。

Usage of Benchmarking Tool WRK and WRK2

wrk 是一款短小精悍又备受赞誉的开源性能测试工具,能够用来对 HTTP 服务进行压测;wrk2 是 对 wrk 的改进,增加了压测结果的直方图输出。

网路上有不少介绍 wrkwrk2 的文章,但大多泛泛而谈,对于压测的结果输出项的解释也是云 里雾里或者简单跳过,殊为遗憾。

本文主要介绍 wrkwrk2 的使用,并在阅读源码的基础上对输出结果的项进行解释。

Change Host Key for VBOXSDL

如果平时使用 Linux,偶尔会有使用 Windows 软件(如 WPS/QQ 等)的需求场景时,最好的解决方案就是使用虚拟机了。如果使用了 Virtual Box 的话,那么可以了解一下其除了基于 Qt 的虚拟机运行界面之外还额外提供了一个基于 SDL 的虚拟机运行界面。

我在使用 virtualbox 的管理界面创建配置好一个基于 Windows 7 的虚拟机 "se7en" 之后,可以直接在终端运行:

${VBOXSDL} --startvm se7en

界面非常简洁,不依赖 Qt

${VBOXSDL} 是 vboxsdl 的可执行文件名;根据 VirtualBox 的安装方式不同,可能会是 "VBoxSDL" 或 "vboxsdl"。

如果需要修改 SDL 界面下的 host 键,可以使用参数 "--hostkey"。

# Set Host key for vboxsdl
# LSHIFT = 304 1
# RSHIFT = 303 2
# LCTRL = 301 8192
# RCTRL = 305 128
# LALT = 308 256
# RALT = 27 0
${VBOXSDL} --hostkey 308 256 --startvm se7en

该命令会设置 host 键为 左-Alt 键并启动虚拟机。