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 序列化方案。

🔗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.6AutoValue: 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


以上。