Preface Introduction Lombok is a handy tool, just like Google Guava, and is highly recommended. Every Java engineer should use it. Lombok is a Java™ utility that helps developers eliminate Java verbosity, especially for plain old Java objects (POJOs). It does this through annotations. By implementing Lombok in their development environment, developers can save a lot of time building methods such as hashCode() and equals(), as well as the time it would take to catalog various accessors and mutators. How is Lombok implemented? import lombok.Getter; import lombok.Setter; @Getter @Setter public class UserInfo { private String userId; private String userName; } Open the UserInfo.class file generated after compilation It is found that get and set methods have been generated, from which we can infer that Lombok enhances the code at compile time. So how is the enhancement at compile time achieved? Compile phase The JSR-269 proposal was proposed and passed in JDK6. The proposal passed a set of standard APIs called "plug-in annotation processors", which can process specific annotations in the code in advance during compilation, thereby affecting the working process of the compiler.
To sum up, if you want to achieve the effect of Lombok, you only need to comply with JSR-269 and operate on AST during compilation. Of course, not only Lombok can be implemented in this way, for example, FindBug, MapStruct, etc. are also implemented in this way. accomplishJCTree JCTree is the base class of AST elements. To achieve the effect, you only need to add a JCTree node. From the class name, you can guess what node is used. Here are a few common explanations TreeMakerMainly used to generate syntax tree nodes CodeFirst, you need to annotate the class to indicate the scope and duration of the action. The two classes correspond to Lombok's Getter and Setter respectively. @Target({ElementType.TYPE}) //Annotation added to the class @Retention(RetentionPolicy.SOURCE) //Affects the compilation period public @interface Getter { } @Target({ElementType.TYPE}) //Annotation added to the class @Retention(RetentionPolicy.SOURCE) //Affects the compilation period public @interface Setter { } To create a new abstract annotation processor, you need to inherit AbstractProcessor. Here, the template method mode is used. public abstract class MyAbstractProcessor extends AbstractProcessor { //Syntax tree protected JavacTrees trees; //Build syntax tree node protected TreeMaker treeMaker; //Create an object of identifier protected Names names; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.trees = JavacTrees.instance(processingEnv); Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); this.treeMaker = TreeMaker.instance(context); this.names = Names.instance(context); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { //Get the annotated class Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(getAnnotation()); //Get the syntax tree set.stream().map(element -> trees.getTree(element)).forEach(jcTree -> jcTree.accept(new TreeTranslator() { //Get the class definition @Override public void visitClassDef(JCTree.JCClassDecl jcClassDecl) { List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil(); //Get all member variables for (JCTree tree: jcClassDecl.defs) { if (tree.getKind().equals(Tree.Kind.VARIABLE)) { JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree; jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl); } } jcVariableDeclList.forEach(jcVariableDecl -> { jcClassDecl.defs = jcClassDecl.defs.prepend(makeMethodDecl(jcVariableDecl)); }); super.visitClassDef(jcClassDecl); } })); return true; } /** * Creation method * @param jcVariableDecl * @return */ public abstract JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl); /** * What kind of annotation to get * @return */ public abstract Class<? extends Annotation> getAnnotation(); } Used to process Setter annotation inheritance MyAbstractProcessor @SupportedAnnotationTypes("com.ingxx.processor.Setter") //The annotation processor acts on which annotation. You can also rewrite getSupportedAnnotationTypes @SupportedSourceVersion(SourceVersion.RELEASE_8) //Can handle any version and can also override getSupportedSourceVersion public class SetterProcessor extends MyAbstractProcessor { @Override public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) { ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>(); //Generate function body this.name = name; 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()); //Generate method return treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), //Access flag getNewMethodName(jcVariableDecl.getName()), //Name treeMaker.TypeIdent(TypeTag.VOID), //Return type List.nil(), //Generic parameter list List.of(getParameters(jcVariableDecl)), //Parameter list List.nil(), //Exception list body, //Method body null //Default method (may be the default in interface) ); } @Override public Class<? extends Annotation> getAnnotation() { return Setter.class; } private Name getNewMethodName(Name name) { String fieldName = name.toString(); return names.fromString("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length())); } private JCTree.JCVariableDecl getParameters(JCTree.JCVariableDecl prototypeJCVariable) { return treeMaker.VarDef( treeMaker.Modifiers(Flags.PARAMETER), //Access flag prototypeJCVariable.name, //Name prototypeJCVariable.vartype, //Type null //Initialization statement); } } Used to process Getter annotations inherited from MyAbstractProcessor @SupportedAnnotationTypes("com.ingxx.processor.Getter") //The annotation processor acts on which annotation. You can also rewrite getSupportedAnnotationTypes @SupportedSourceVersion(SourceVersion.RELEASE_8) //Can handle any version and can also override getSupportedSourceVersion public class GetterProcessor extends MyAbstractProcessor { @Override public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) { ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>(); //Generate function body return this.Field name statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()))); JCTree.JCBlock body = treeMaker.Block(0, statements.toList()); //Generate method return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null); } @Override public Class<? extends Annotation> getAnnotation() { return Getter.class; } private Name getNewMethodName(Name name) { String fieldName = name.toString(); return names.fromString("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length())); } } Compiling an existing class javac -cp $JAVA_HOME/lib/tools.jar *.java -d . Create a new test class. In IDEA, an error will be reported because the get set method cannot be found. You can ignore it. @Getter @Setter public class UserInfo { private String userId; private String userName; public static void main(String[] args) { UserInfo userInfo = new UserInfo(); userInfo.setUserId("001"); userInfo.setUserName("Dewu"); System.out.println("id = "+userInfo.getUserId()+" name = "+userInfo.getUserName()); } } Then compile //Multiple processors are separated by commas javac -processor com.ingxx.processor.GetterProcessor,com.ingxx.processor.SetterProcessor UserInfo.java -d . Check the compiled file and find that get and set methods have been generated The above is the details from Lombok to JSR-269. For more information about JS reflection mechanism, please pay attention to other related articles on 123WORDPRESS.COM! You may also be interested in:
|
<<: Detailed analysis of MySQL instance crash cases
>>: How to run commands on a remote Linux system via SSH
1. Development environment vue+vant 2. Computer s...
After I set up the PHP development environment on...
Introduction Because JavaScript is single-threade...
Today, after the game was restarted, I found that...
<br />When thoughts were divided into East a...
Introduction to CentOS CentOS is an enterprise-cl...
1. Export the database using the mysqldump comman...
background During the project development process...
Table of contents 1. Dockerfile 2. pom configurat...
Preface Mobile devices have higher requirements f...
This article mainly introduces the installation/st...
Table of contents 1. Communication method between...
Use Javascript to implement a drop-down menu for ...
Table of contents What is maintainable code? Code...
Without further ado, I will post the code for you...