Before we begin, let us first affirm a very important question: we do not discuss annotations that run processing at runtime (runtime) through the reflection mechanism, but rather on annotations that are processed at compile time (Compile. The annotation processor is a tool that is used in Javac to scan and process annotations at compile time. You can register your own annotation processor for specific annotations.
An annotated note processor that takes Java code (or compiled bytecode) as input, generating files (usually. java files) as output. Java code can be generated, and the generated Java code is in the generated. java file, so you cannot modify a Java class that already exists, such as adding a method to an existing class. These generated Java files are javac compiled in the same way as other normal, manually written Java source code.
Virtual Processor Abstractprocessor
Let's look at the processor's API first. Each processor is inherited from the Abstractprocessor, as follows:
public Class Myprocessor extends Abstractprocessor {@Override public synchronized void init (Processingenvironment env) {} @Override public Boolean process (SET<? extends typeelement> annoations, roundenvironment env) {} @Override Public set<string> Getsupportedannotationtypes () {} @Override public sourceversion getsupportedsourcevers Ion () {}}
- init (processingenvironment env): Each annotation processor class must have an empty constructor . However, there is a special init () method, which is called by the annotation processing tool and enters the processingenviroment parameter. Processingenviroment offers many useful tools such as elements,types and filer.
- process (set<? extends typeelement> Annotations, Roundenvironment env): This corresponds to the main function of each processor main (). Write code to scan, evaluate, and process annotations here, as well as generate Java files. Input parameter roundenviroment lets you query out annotated elements that contain specific annotations.
- getsupportedannotationtypes (): Must be specified here, which annotation the annotation processor is registered to. Note that its return value is a collection of strings containing the legal full name of the type of annotations the processor wants to process. In other words, here you define which annotations your annotation processor is registered to.
- getsupportedsourceversion (): Used to specify the Java version you are using. Usually return here sourceversion.latestsupported (). However, if there are enough reasons to support Java 6 only, you can return to Sourceversion.release_6. It is recommended to use the former.
Give a simple example
Automatically generate a bean's structure file
Put public class Student {public string stu_name;public string stu_id;public int stu_age;} Convert to {class: "Com.robert.processor.Student", fields : { stu_name: "java.lang.String", stu_id: " Java.lang.String ", stu_age:" Java.lang.Integer "}
First DECLARE annotations
Package Com.robert.processor;import Java.lang.annotation.elementtype;import Java.lang.annotation.retention;import Java.lang.annotation.retentionpolicy;import Java.lang.annotation.Target; @Target ({Elementtype.field, Elementtype.type}) @Retention (retentionpolicy.class) public @interface Serialize {}
Add annotations to the student class
@Serialize
public class Student
Define your own parser
Package Com.robert.processor;import Java.io.file;import Java.io.filewriter;import java.io.ioexception;import Java.util.arraylist;import java.util.hashmap;import java.util.hashset;import Java.util.list;import java.util.Map; Import Java.util.set;import Javax.annotation.processing.abstractprocessor;import Javax.annotation.processing.processingenvironment;import Javax.annotation.processing.roundenvironment;import Javax.lang.model.element.element;import Javax.lang.model.element.elementkind;import Javax.lang.model.element.typeelement;import Javax.lang.model.element.variableelement;import Javax.lang.model.util.elementfilter;import Javax.lang.model.util.elements;public class MyProcessor extends Abstractprocessor {///element operation elements elementutils; @Overridepublic synchronized void init (processingenvironment processingenv) {super.init (processingenv); elementutils = Processingenv.getelementutils ();} @Overridepublic Boolean process (set<? extends typeelement> annotations, roundenvironment RoundenV) {//Obtain the element set< by the annotation declaration;? extends element> Elememts = Roundenv.getelementsannotatedwith (Serialize.class); Typeelement classelement = null;//declares the class element list<variableelement> fields = null;//declares a list of member variables to hold//store both map<string, list<variableelement>> maps = new hashmap<string, list<variableelement>> ();//Traverse for (Element ele: ELEMEMTS) {//Determines whether the element is a class if (ele.getkind () = = Elementkind.class) {classelement = (typeelement) ele;maps.put ( Classelement.getqualifiedname (). toString (), fields = new arraylist<variableelement> ());} else if (ele.getkind () = = Elementkind.field)//Determines whether the element is a member variable {variableelement Varele = (variableelement) ele;//Gets the element encapsulation type T Ypeelement enclosingelement = (typeelement) varele.getenclosingelement ();//get keystring key = Enclosingelement.getqualifiedname (). toString (); fields = Maps.get (key), if (n = null) {maps.put (key, fields = new Arraylist<variableelement> ());} Fields.Add (Varele);}} For (String Key:maps.keySet ()) {if (Maps.get (key). Size ()= = 0) {typeelement typeelement = elementutils.gettypeelement (key); list<? Extends element> allmembers = elementutils.getallmembers (typeelement); if (allmembers.size () > 0) {maps.get (key). AddAll (Elementfilter.fieldsin (AllMembers));}} Generatefile (maps); return true;} private void Generatefile (map<string, list<variableelement>> maps) {file Dir = new File ( MyProcessor.class.getResource ("/"). GetPath ()); if (!dir.exists ()) dir.mkdirs ();//Traverse Mapfor (String key:maps.keySet () ) {//create file filename = ' new file ' (dir, key.replaceall ("\ \", "_") + ". txt"); try {/** * Write file contents */filewriter fw = new FileWrite R (file), Fw.append ("{"). Append ("Class:"). Append ("\" "+ key +" \ "). Append (", \ n "); Fw.append (" fields:\n {\ n "); list<variableelement> fields = Maps.get (key), for (int i = 0; i < fields.size (); i++) {variableelement field = Fie Lds.get (i); Fw.append (""). Append (Field.getsimplename ()). Append (":"). Append ("\" + Field.astype (). toString () + "\" ") if (I < fields.size ()-1) {Fw.append (","); Fw.append (" \ n ");}} Fw.append ("\ n}\n"); Fw.append ("}"); Fw.flush (); Fw.close ();} catch (IOException e) {e.printstacktrace ();}}} @Overridepublic set<string> getsupportedannotationtypes () {set<string> Set = Super.getsupportedannotationtypes (); if (set = = null) {set = new hashset<> ();} Set.add ("Com.robert.processor.Serialize"); return set;}}
The butterknife that we often use is a good framework for using abstractprocessor
Butter Knife is an Android view field and method binding that uses annotation processing to generate boilerplate code. Detailed instructions later.
Welcome to scan QR Code, follow the personal public number
Java compile-time annotations automatically generate code