Notes resolved at compile time to Lombok

Previous article introduced Java annotations relevant content (see Java Annotations Annotation processing time), this article will introduce and compile Lombok behind.

Generating POJO setter / getter by annotating

First define an annotation @Data:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
}
复制代码

The annotation processor to achieve @Data

public class DataProcessor extends AbstractProcessor {

    private Messager messager;
    private JavacTrees trees;
    // 创建AST节点工具
    private TreeMaker treeMaker;
    private Names names;

    /** 获取处理器框架组件 */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);
    }

    /** 注解处理逻辑,在这里修改AST */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Kind.NOTE, "starting process");
        /** 获取Data注解的元素 */
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Data.class);
        for (Element element : elements) {
            JCTree jcTree = trees.getTree(element);
            // 遍历类定义的元素
            jcTree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCClassDecl jcClassDecl) {
                    super.visitClassDef(jcClassDecl);
                    try {
                        java.util.List<JCVariableDecl> jcVariableDeclList = new ArrayList<>();
                        for (JCTree tree : jcClassDecl.defs) {
                            // 收集所有field
                            if (tree.getKind() == Tree.Kind.VARIABLE) {
                                jcVariableDeclList.add((JCVariableDecl) tree);
                            }
                        }

                        for (JCVariableDecl jcVariableDecl : jcVariableDeclList) {
                            // 生成相应的geeter,setter
                            generateElement(jcClassDecl, jcVariableDecl);
                        }
                    } catch (Throwable e) {
                        try {
                            e.printStackTrace(new PrintStream(new FileOutputStream("error.txt")));
                        } catch (FileNotFoundException e1) {
                            messager.printMessage(Kind.ERROR, "Error create log file");
                        }
                    }
                }
            });
        }
        return true;
    }

    /** 处理的注解类型 */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        LinkedHashSet<String> supportedAnnotations = new LinkedHashSet<>();
        supportedAnnotations.add("*");
        return supportedAnnotations;
    }

    /** 支持的JDK版本 */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
复制代码

Specific method of generating logic:

private void generateElement(JCClassDecl jcClassDecl, JCVariableDecl jcVariableDecl) throws Exception {
        // 将生成的getter方法插入类语法树中
        JCMethodDecl getter = generateGetterMethodElement(jcVariableDecl);
        jcClassDecl.defs = jcClassDecl.defs.append(getter);

  			// 将生成的setter方法插入类语法树中
        JCMethodDecl setter = generateSetterMethodElement(jcVariableDecl);
        jcClassDecl.defs = jcClassDecl.defs.append(setter);
    }

		/** 生成getter方法 */
    private JCMethodDecl generateGetterMethodElement(JCVariableDecl jcVariableDecl) {

        String field = jcVariableDecl.getName().toString();
        Name name = names.fromString("get" + field.substring(0, 1).toUpperCase() + field.substring(1, field.length()));

        ListBuffer<JCStatement> statements = new ListBuffer<>();
        statements.append(
            treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), name, jcVariableDecl.vartype,
            List.nil(), List.nil(), List.nil(), body, null);
    }

		/** 生成setter方法 */
    private JCMethodDecl generateSetterMethodElement(JCVariableDecl jcVariableDecl) throws Exception {

        String field = jcVariableDecl.getName().toString();
        Name name = names.fromString("set" + field.substring(0, 1).toUpperCase() + field.substring(1, field.length()));

        JCTree.JCExpression returnMethodType = treeMaker
            .Type((Type) (Class.forName("com.sun.tools.javac.code.Type$JCVoidType").newInstance()));

        ListBuffer<JCStatement> statements = new ListBuffer<>();
        statements.append(treeMaker.Exec(treeMaker
            .Assign(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()),
                treeMaker.Ident(jcVariableDecl.getName()))));
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        JCTree.JCVariableDecl param = treeMaker
            .VarDef(treeMaker.Modifiers(Flags.PARAMETER, List.nil()), jcVariableDecl.name, jcVariableDecl.vartype,
                jcVariableDecl.nameexpr);
        List<JCTree.JCVariableDecl> parameters = List.of(param);

        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), name, returnMethodType,
            List.nil(), parameters, List.nil(), body, null);
    }
复制代码

With @Data annotation to modify a class:

@Data
public class Member {
    private Long id;
    private String name;
    private Date joinDate;
}
复制代码

Get! Verify:

javac DataProcessor.java && javac -g -processor DataProcessor Member.java && javap -p Member
复制代码

SPI packaged into kits by

maven build configuration:

<build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <excludes>
          <!-- 构建时排除Service声明,此时Processor还不可用 -->
          <exclude>META-INF/**/*</exclude>
        </excludes>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
        <executions>
          <execution>
            <!-- prepare-package阶段再把services拷贝到生成类目标目录 -->
            <id>process-META</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <outputDirectory>target/classes</outputDirectory>
              <resources>
                <resource>
                  <directory>${basedir}/src/main/resources/</directory>
                  <includes>
                    <include>**/*</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
复制代码

New projects, introduction of dependence:

<dependency>
  <groupId>org.yannis</groupId>
  <artifactId>srctool</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
复制代码

Copy Member.java to the new items:

mvn clean packageObserved target generated class Member.class decompile Results:

So far, it has achieved a simple function of lombok!

chilli

Lombok is what I prefer in the individual events with a tool that can enable us to generate a series of class boilerplate code at compile time by simply notes, source code can be kept very simple. Following is a brief introduction of several commonly used notes, you can view more Quguan network usage.

Dependency:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
    <scope>provided</scope>
</dependency>
复制代码

Note: the mounting plug Lombok idea, eclipse ide and the like, you can see the method of generating a direct

@Getter/@Setter

Generating an instance field getter / setter method, the default is public, access control level may be changed by AccessLevel. Properties can be identified by the prefix @Accessors (prefix = "m").

@ToString

Generating object toString () method, by exclude field is set to be excluded.

@EqualsAndHashCode

Generating object hashCode () and equals () method, exclude provided to exclude field

@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

No parameters generated with the parameter (final modified attributes or @NotNull) constructor, the whole argument constructor.

@Data

Corresponds @ ToString, collection @ EqualsAndHashCode, @ Getter / @Setter @RequiredArgsConstructor and the effect is as follows:

@Builder

Constructor for the class to generate a static class XxxBuilder, commonly

@Log

The introduction of the log, very convenient

Finish!

Past articles:

All articles micro-channel public number of first time update

Guess you like

Origin juejin.im/post/5d6fa1916fb9a06af471e10a