From compile-time annotation parsing to Lombok

Links to the original text:

Previous articles have covered Java annotations (see Java Annotations This article will introduce Lombok and its underlying compile-time annotation processing technology.

(If you want to learn programming by yourself, please search Circle T Community More industry-related information and more industry-related free video tutorials. It's totally free!

Generating POJO setter/getter through annotations

First, define an annotation @Data:

public @interface Data {

Annotation Processor Implementing @Data

public class DataProcessor extends AbstractProcessor {

    private Messager messager;
    private JavacTrees trees;
    // Creating AST Node Tool
    private TreeMaker treeMaker;
    private Names names;

    /** Getting Processor Framework Components */
    public synchronized void init(ProcessingEnvironment processingEnv) {
        messager = processingEnv.getMessager();
        trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);

    /** Annotation processing logic, modify AST here */
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Kind.NOTE, "starting process");
        /** Get the elements of the Data annotation */
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Data.class);
        for (Element element : elements) {
            JCTree jcTree = trees.getTree(element);
            // Traversing through elements defined by a class
            jcTree.accept(new TreeTranslator() {
                public void visitClassDef(JCClassDecl jcClassDecl) {
                    try {
                        java.util.List<JCVariableDecl> jcVariableDeclList = new ArrayList<>();
                        for (JCTree tree : jcClassDecl.defs) {
                            // Collect all field s
                            if (tree.getKind() == Tree.Kind.VARIABLE) {
                                jcVariableDeclList.add((JCVariableDecl) tree);

                        for (JCVariableDecl jcVariableDecl : jcVariableDeclList) {
                            // Generate corresponding 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;

    /** Annotation types of processing */
    public Set<String> getSupportedAnnotationTypes() {
        LinkedHashSet<String> supportedAnnotations = new LinkedHashSet<>();
        return supportedAnnotations;

    /** Supported JDK version */
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();

Specific method generates logic:

private void generateElement(JCClassDecl jcClassDecl, JCVariableDecl jcVariableDecl) throws Exception {
        // Insert the generated getter method into the class syntax tree
        JCMethodDecl getter = generateGetterMethodElement(jcVariableDecl);
        jcClassDecl.defs = jcClassDecl.defs.append(getter);

  			// Insert the generated setter method into the class syntax tree
        JCMethodDecl setter = generateSetterMethodElement(jcVariableDecl);
        jcClassDecl.defs = jcClassDecl.defs.append(setter);

		/** Generating getter method */
    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<>();
            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);

		/** Generating setter method */
    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("$JCVoidType").newInstance()));

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

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

        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), name, returnMethodType,
            List.nil(), parameters, List.nil(), body, null);

Decorate a class with the @Data annotation:

public class Member {
    private Long id;
    private String name;
    private Date joinDate;

Get it done! Verify that:

javac && javac -g -processor DataProcessor && javap -p Member

Toolkit encapsulation via SPI

maven build configuration:

          <!-- Exclusion at Construction Service Statement, at this time Processor Not yet available -->
            <!-- prepare-package The stage then copies services to the generated class target directory - >.

New projects, introducing dependencies:


Copy to the new project:

mvn clean package, observe the decompilation result of target generating class Member.class:

So far, a simple lombok function has been implemented!


Lombok is my favorite tool in personal projects. It enables us to generate a series of boilerplate code for classes at compile time by simple annotations, while the source code can be kept very simple. Following is a brief introduction to some commonly used annotations, more usage can be seen on the official website.



Note: Install Lombok plug-ins in ides such as idea, eclipse, etc., and you can see the generated method directly.


Generate getter/setter method for instance field, default is public, access control level can be changed through AccessLevel. Attribute prefixes can be identified by @Accessors(prefix = m).


The toString() method that generates the object sets the fields that need to be excluded through exclude.


The hashCode() and equals() methods for generating objects set the fields to be excluded by exclude

@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

Generate a parametric, full-parameter constructor with parameters (final or @NotNull-modified attributes).


Equivalent to @ToString, @Equals AndHashCode, @Getter/ Setter and @RequiredArgsConstructor collections, the effect is as follows:


Generate static constructor class XxBuilder for classes, commonly used


Introducing logs is very convenient


Tags: Java Lombok Maven Apache

Posted on Wed, 04 Sep 2019 20:21:02 -0700 by gordonrp