JavaPoet使用介绍

作者: 王岩_shang | 来源:发表于2017-06-09 16:41 被阅读79次

    Square 出品,必属精品

    没错,有事没事就要抱大腿,JavaPoet是以面向对象的方式来生成.java源代码的一个api库。

    背景

    在使用APT生产Java源码的过程中,我们通常的做法是手动写代码,但是这样确实不符合程序员的性格啊,毕竟手动填进去的代码,万一少个括号或者分号,肯定编译不过去,还要再改。
    恩,JavaPoet因此应运而生。具体原理这里就不讲了,就是生产字符串信息,而其中的背景以及设计的思路确实是值得学习的。当时不知道这个类库的时候还傻傻的一行一行写字符串... (捂脸逃

    实际应用

    下面举个栗子:

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

    其对应的生产方式代码如下:

    //方法生产
    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();
    //.java 文件生产
    JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
        .build();
    

    当我们的类方法体里面并没有模板的方法,类型或者参数表达式,是固定不变的,我们可以这样写:

    MethodSpec main = MethodSpec.methodBuilder("main")
        .addCode(""
            + "int total = 0;\n"
            + "for (int i = 0; i < 10; i++) {\n"
            + "  total += i;\n"
            + "}\n")
        .build();
    

    生产的代码是这样的:

    void main() {
      int total = 0;
      for (int i = 0; i < 10; i++) {
        total += i;
      }
    }
    

    或者有参数是可变的,这样写:

    private MethodSpec computeRange(String name, int from, int to, String op) {
      return MethodSpec.methodBuilder(name)
          .returns(int.class)
          .addStatement("int result = 0")
          .beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)")
          .addStatement("result = result " + op + " i")
          .endControlFlow()
          .addStatement("return result")
          .build();
    }
    

    然后调用:

    computeRange("multiply10to20", 10, 20, "*")
    

    生产的代码是这样的:

    int multiply10to20() {
      int result = 0;
      for (int i = 10; i < 20; i++) {
        result = result * i;
      }
      return result;
    }
    

    以上大概是如何使用的一些栗子,然后我们来想一下这个api是如何实现的。

    我要如何写出这样的被STAR 3500以上的API

    哈哈,这个标题写的有点重了,不过还是大致说下:

    1. 坚持。 肯定不是一蹴而就的,这个项目被维护了两年,到现在还一直有提交。可是国内的某些githuber ,一次提交就完事了,以后再不更新,有人提isuue 也爱答不理,功利心太强
    2. 想法很重要,行动更重要。这里说白了就是字符串拼接,但是人家发现了这个问题,并且切实的解决了这个问题,确实是高。Let me read the fucking code
    3. java类结构分析
      一个.java 文件,大致可以做一下切分如下图:
    一个java类

    然后我们来看下注解是如何添加到方法,变量以及类上面的。
    在FiledSpec中,通过一个建造者模式,添加一个AnnotationSpec

    
        public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
          checkArgument(annotationSpecs != null, "annotationSpecs == null");
          for (AnnotationSpec annotationSpec : annotationSpecs) {
            this.annotations.add(annotationSpec);
          }
          return this;
        }
    

    在MethodSpec 和 TypeSpec中,同样的:

     public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
          checkArgument(annotationSpecs != null, "annotationSpecs == null");
          for (AnnotationSpec annotationSpec : annotationSpecs) {
            this.annotations.add(annotationSpec);
          }
          return this;
        }
    

    在FliedSpec中生产代码:

      void emit(CodeWriter codeWriter, Set<Modifier> implicitModifiers) throws IOException {
        codeWriter.emitJavadoc(javadoc);
        codeWriter.emitAnnotations(annotations, false);
        codeWriter.emitModifiers(modifiers, implicitModifiers);
        codeWriter.emit("$T $L", type, name);
        if (!initializer.isEmpty()) {
          codeWriter.emit(" = ");
          codeWriter.emit(initializer);
        }
        codeWriter.emit(";\n");
      }
    
    

    这里我们分析下实现:可以看到,根据上面的图例,合理的抽象出对象,使用合理的设计模式,写的代码也易于理解。确实有很多值得借鉴和学习的地方。

    总结

    临渊羡鱼不如退而结网,共勉之。

    参考

    相关文章

      网友评论

        本文标题:JavaPoet使用介绍

        本文链接:https://www.haomeiwen.com/subject/ztjfqxtx.html