Value Class Generated by Google AutoValue
Java 中组织数据对象的方式中,最简单也是最通用的是所谓的 POJO (Plain Ordinary Java Object) 即 Java 简单对象。POJO 大法好,好就好在简单实用:只需用遵循简单的命名约定和构造规则,就可以满足数据实体 的需求,而且大多数的序列化方案和持久化方案都能提供 POJO 的支持;如果追求代码精简的话,使用 POJO 可以 和反射方案一起极大地简化重复代码。
POJO 的好处吾等皆非常清楚,然而本文却在讲 AutoValue,因为:
- 人是善变的。吾等吃够了鸡蛋牛奶,也会想要尝尝豆浆油条。
equals
toString
hash
。此三种样板代码,即使是有 IDE 的协助,维护起来仍会非常痛苦。- Mutable shared state is the root of all evil in concurrent systems 。可变性在并发系统中的共享是所有 BUG 的源头,不可变性是救赎之道;而 POJO 不能满足不可变性约束。
- Google 大法好。AutoValue 是 Google Java 团队的作品。
AutoValue 是一个基于 Java 注解处理器 APT (Annotation Processing Tool) 的代码生成库,网络上关于 AutoValue 的介绍不要太少,毋庸再讲;其理念和实际用法可见 官网文档,十分钟入门,老少咸 宜。
本文对于 AutoValue 的用法做一个简单汇总,并引申介绍一下基于 AutoValue 的对 象 JSON 序列化方案。
🔗AutoValue Usecases
🔗Maven Settings
<dependency> <groupId>com.google.auto.value</groupId> <artifactId>auto-value</artifactId> <version>${auto-value.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-annotations</artifactId> <version>${auto-value.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.squareup.auto.value</groupId> <artifactId>auto-value-redacted</artifactId> <version>${squareup_autovalue_redacted_version}</version> <scope>provided</scope> </dependency>
auto-value-annotations
包含定义数据实体类时需要的注解,auto-value
包含注解处理器实现。如果需要在 toString
的实现中对某些字段作特殊处理(比如
密码字段需要隐藏),可以额外使用 AutoValue: Redacted
Extension。
截至 2019-09-28 日,AutoValue 的最新版本是 1.6.6
,AutoValue:
Redacted Extension 的最新版本是
1.1.1
。
🔗Abstract Class
一个基本的数据类如下:
@AutoValue public abstract class Task { public abstract Long id(); public abstract String type(); public abstract String description(); public static Task create(Long id, String type, String description) { return new AutoValue_Task(id, type, description); } }
上述是所需要的全部代码。
定义一个数据实体类,只需以抽象方法的方式定义所需字段即可,AutoValue 会通过 APT 生成一个名为 AutoValue_Task
的实现类。类名 AutoValue_Task
是根据数据实体类名 Task
按照固定规则推导得到的。
🔗Builder
如果字段项很多的话,可以使用 Builder 模式。
@AutoValue abstract class StudentDto { public abstract String getName(); public abstract Level getLevel(); public abstract OffsetDateTime getBirthday(); public abstract String getAddr(); public abstract List<String> getEmails(); public abstract Map<String, Integer> getScores(); public abstract Integer getAdditionalInt(); public abstract List<String> getAdditionalList(); public abstract Map<String, Integer> getAdditionalMap(); public static Builder builder() { return new AutoValue_StudentDto.Builder(); } @AutoValue.Builder abstract static class Builder { public abstract Builder setName(String name); public abstract Builder setLevel(Level level); public abstract Builder setBirthday(OffsetDateTime birthday); public abstract Builder setAddr(String addr); public abstract Builder setEmails(List<String> emails); public abstract Builder setScores(Map<String, Integer> scores); public abstract Builder setAdditionalInt(Integer additionalInt); public abstract Builder setAdditionalList(List<String> additionalList); public abstract Builder setAdditionalMap(Map<String, Integer> additionalMap); public abstract StudentDto build(); } }
Builder 模式 通过在数据类 中定义一个抽象的 Builder 类实现,Builder 类的实现由 AutoValue 自动生成。
AutoValue 对 Java 的集合类型有很好的支持,配合 Guava 不可变集合 一起食用味道更加。
🔗Json Support - JSON-B
JSON-B 是 Java EE (现 Jakarta) 标准的对象与 JSON 之间的序列化/反序列化的标准 API;参考实现是 Yasson,不过我一直使用 Apache Johnzon 的实现。
- 对于 Apache Johnzon 的实现来说,AutoValue 生成的对象可以直接通过 JSON-B 实现序列化,不需要作其他修改;反序列化则不支持。
- 对于 Yasson 的实现来说,AutoValue 生成的对象无法直接通过 JSON-B 实现序列化;反序列化则可以通过在 Builder 中手动添加
JsonbCreator
的静态构造方法的方式,通过 JSON-B 先构造 Builder 实例再调用build()
方法的方式间接实现。
结论:可以直接手写序列化的代码而不是依赖 JSON-B 来实现序列化和反序列化;或者,可以定义一个专门用来序 列化的 POJO 类做一个中间层,AutoValue 的实体类和 POJO 实体类之间的转化可以通过 之类的框架实现。
🔗Json Support - Moshi
使用 AutoValue 的数据实体类如果有很强的 JSON 的序列化/反序列化需求的话,可以使用基于 Moshi: a modern JSON library for Android and Java 的 AutoValue 扩展 AutoValue: Moshi Extension 来实现。
🔗Json Support - Gson
或者,也可以使用基于 Gson 的 AutoValue 扩展 AutoValue: Gson Extension 来实现。
🔗类似项目
🔗FreeBuilder
FreeBuilder 是另一个基于注解处理器 APT 的实体类框架,主要是实现 了 Builder 模式。
其与 AutoValue 的区别在于,AutoValue 由用户定义接口委托给注解处理器去实现,而 FreeBuilder 则是预定义了一整套的基于属性(Property)的 Builder 接口,用户使用时需要去继承之。也就是说 ,前者可以选择暴露出的 Builder API,后者则提供了丰富的(如果不是极简主义者的话)的 Builder API。
如果想要对比的话,可以参考 FreeBuilder Alternatives 。
🔗Immutables
Immutables 是另一个拥有 Immutable & Builder 标签的类库。
如果想要对比的话,可以参考 FreeBuilder Alternatives 。
🔗Lombok
Lombok 是一个极富争议的代码生成库,争议点在于其为了提高生产力(想想那 么多的样板重复代码 Getter Setter 之类的)而改变了 Java 的语法。
其他的类库基于注解处理器的标准 API 进行代码生成,不会改变已有的类(通常是生成一个抽象类或接口的实现 或生成一个标准的样本类);而 Lombok 则会在 APT 标准 API 之外依赖 Orcle JDK 的一些特定 API 侵入代码, 修改源代码的结构。
这样的一个好处是,能有效地解决 Java 开发的一些痛点;坏处则是自己定义了一门新的语言。
🔗AutoFactory
AutoFactory 是另一个 Google 的代码生成类库,用来生成工厂类的。
具体的代码可以参见 AutoValue Usecases 。
以上。