JavaPoet - 优雅地生成代码

JavaPoet是square的开源框架,用于优雅地生成Java代码。它提供了API来创建.java文件,简化了根据注解、数据库模式、协议生成代码的过程。通过JavaFile、TypeSpec、MethodSpec和FieldSpec等类,JavaPoet实现了代码的层次化构造。项目结构简单,主要类都在com.squareup.javapoet包下。通过注解处理器,可以在编译时动态生成代码,例如在butterknife和Dagger中就应用了JavaPoet。
摘要由CSDN通过智能技术生成

JavaPoet - 优雅地生成代码


一、项目简介

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。

项目主页及源码:https://github.com/square/javapoet

二、项目总览

该项目代码量相对较小,只有一个package(com.squareup.javapoet),所有类均位于该package下。

2.1 大体结构图

JavaFile.png

2.2 关键类说明

class 说明
JavaFile A Java file containing a single top level class 用于构造输出包含一个顶级类的Java文件
TypeSpec A generated class, interface, or enum declaration 生成类,接口,或者枚举
MethodSpec A generated constructor or method declaration 生成构造函数或方法
FieldSpec A generated field declaration 生成成员变量或字段
ParameterSpec A generated parameter declaration 用来创建参数
AnnotationSpec A generated annotation on a declaration 用来创建注解

在JavaPoet中,JavaFile是对.java文件的抽象,TypeSpec是类/接口/枚举的抽象,MethodSpec是方法/构造函数的抽象,FieldSpec是成员变量/字段的抽象。这几个类各司其职,但都有共同的特点,提供内部Builder供外部更多更好地进行一些参数的设置以便有层次的扩展性的构造对应的内容。

另外,它提供$L(for Literals), $S(for Strings), $T(for Types), $N(for Names)等标识符,用于占位替换。

三、相关使用

3.1 API使用

关于JavaPoet 的API使用,官方Github主页已经有很详细的使用说明和示例了,具体可前往查看。此处不赘述,详见 项目主页、源码及使用说明

3.2 一个简单示例

下面就让我们以一个简单HelloWorld的例子来开启我们的JavaPoet之旅。

引入库:
build.gradle

compile 'com.squareup:javapoet:1.9.0'

例子如下:

package com.example.helloworld;

public final class HelloWorld {
   
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet!");
  }
}

上方的代码是通过下方代码调用JavaPoet的API生成的:

MethodSpec main = MethodSpec.methodBuilder("main")
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    .returns(void.class)
    .addParameter(String[].class, "args")
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(main)
    .build();

JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    .build();

javaFile.writeTo(System.out);

四、源码浅析

下面来看看调用了JavaFile的writeTo后实际做了些什么。

  public void writeTo(Appendable out) throws IOException {
    // First pass: emit the entire class, just to collect the types we'll need to import.
    CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports);
    emit(importsCollector);
    Map<String, ClassName> suggestedImports = importsCollector.suggestedImports();

    // Second pass: write the code, taking advantage of the imports.
    CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports);
    emit(codeWriter);
  }

通过源码可以知道,writeTo分为两部分:第一步收集import,记录下来后第二步才跟随内容一起写到CodeWriter。

另外我们可以看到源码中的emit方法,通过查看其它源码发现,在JavaPoet中,所有java文件的抽象元素都定义了emit方法,如TypeSepc,ParameterSepc等,emit方法传入CodeWriter对象输出字符串。上层元素调用下层元素的emit方法,如JavaFile的emit方法调用TypeSpec的emit方法,从而实现整个java文件字符串的生成。

下面我们以MethodSpec为例,查看其emit代码:


  void emit(CodeWriter codeWriter, String enclosingName, Set<Modifier> implicitModifiers)
      throws IOException {
    codeWriter.emitJavadoc(javadoc);
    codeWriter.emitAnno
  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用 JavaPoet 代码生成生成序列化和反序列化方法的代码,可以按照以下步骤进行: 1. 添加 JavaPoet 的依赖 在的 build.gradle 文件中以下依赖: ``` { implementation 'com.squareup:javapo:1.13.0' } ``` 2.义要生成代码的类 在 Java 中定义要生成代码的类,可以包含多个字段,其中一些字段的类型是自定义类型本身,如下所示: ``` public class MyObject { private String field1; private int field2; // ... 其他字段 private MyObject field3; private MyObject field4; // ... 其他自定义类型字段 // ... 构造函数、getter 和 setter 方法等 } ``` 3. 使用 JavaPoet 生成代码Java 中使用 JavaPoet 生成序列化和反序列化方法的代码,可以按照以下步骤进行: (1)定义序列化方法的代码模板 ``` MethodSpec.Builder writeMethodBuilder = MethodSpec.methodBuilder("write") .addModifiers(Modifier.PUBLIC) .addParameter(Kryo.class, "kryo") .addParameter(Output.class, "output") .addParameter(MyObject.class, "obj") .returns(void.class); for (Field field : MyObject.class.getDeclaredFields()) { String fieldName = field.getName(); TypeName fieldType = TypeName.get(field.getType()); if (fieldType == TypeName.get(String.class)) { writeMethodBuilder.addStatement("output.writeString(obj.$L)", fieldName); } else if (fieldType == TypeName.INT) { writeMethodBuilder.addStatement("output.writeInt(obj.$L)", fieldName); } else if (fieldType == ClassName.get(MyObject.class)) { writeMethodBuilder.addStatement("kryo.writeObject(output, obj.$L)", fieldName); } // ... 其他类型的处理 } MethodSpec writeMethod = writeMethodBuilder.build(); ``` (2)定义反序列化方法的代码模板 ``` MethodSpec.Builder readMethodBuilder = MethodSpec.methodBuilder("read") .addModifiers(Modifier.PUBLIC) .addParameter(Kryo.class, "kryo") .addParameter(Input.class, "input") .addParameter(Class.class, "type") .returns(MyObject.class); readMethodBuilder.addStatement("$T field1 = input.readString()", String.class); readMethodBuilder.addStatement("$T field2 = input.readInt()", int.class); // ... 其他类型的处理 readMethodBuilder.addStatement("$T field3 = kryo.readObject(input, $T.class)", MyObject.class, MyObject.class); readMethodBuilder.addStatement("$T field4 = kryo.readObject(input, $T.class)", MyObject.class, MyObject.class); // ... 其他自定义类型字段的处理 readMethodBuilder.addStatement("return new MyObject(field1, field2, ..., field3, field4, ...)"); MethodSpec readMethod = readMethodBuilder.build(); ``` 4. 生成 Java 代码 ``` TypeSpec myObjectType = TypeSpec.classBuilder("MyObjectSerializer") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Serializer.class), ClassName.get(MyObject.class))) .addMethod(writeMethod) .addMethod(readMethod) .build(); JavaFile javaFile = JavaFile.builder("com.example.serialization", myObjectType) .build(); javaFile.writeTo(new File("src/main/java")); ``` 在上述代码中,首先通过 TypeSpec.Builder 定义 MyObjectSerializer 类的代码结构,然后使用 JavaFile.builder 生成 Java 文件,并将生成代码写入到指定的文件中。 需要注意的是,上述代码中只是简单示例,实际使用时需要根据自己的需求进行修改和扩展。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值