
一、什么是APT
APT(AnnotationProcessingTool)注解处理器,是javac的一个工具,它可以在源码生成class的时候,处理Java语法树。lombok的原理,在编译期修改字节码,生成get和set方法。
下面我们就使用APT技术,来自动生成get和set方法。附带源码,感兴趣的可以下载学习。
感兴趣的可以私信APT,获取源码地址
本文目录较多不用害怕,尤其是2.6到2.7目录,不需要看到这个API就害怕。也不用死记硬背。了解就好。正经人是不会用这个生成字节码的,字节码生成我们有很多好用的工具。
Javassist或者是javapoet二、实战演示2.1定义处理器继承AbstractProcessor
@AutoService(Processor.class)@SupportedAnnotationTypes({“cn.lxchinesszz.MyData”,“cn.lxchinesszz.MyGetter”,“cn.lxchinesszz.MySetter”})//这个注解处理器是处理哪个注解的@SupportedSourceVersion(SourceVersion.RELEASE_8)publicclassMyLombokProcessorextendsAbstractProcessor{@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnvironment){}@Overridepublicbooleanprocess(Set<?extendsTypeElement>set,RoundEnvironmentroundEnv){}}@AutoService谷歌提供的SPI工具。当使用这个注解会自定生成JavaSPI文件,当然如果不想用谷歌的工具,我们也可以自己来写配置文件
├──classes│├──META-INF││└──services││└──javax.annotation.processing.Processor@SupportedAnnotationTypes({“cn.lxchinesszz.MyData”,“cn.lxchinesszz.MyGetter”,“cn.lxchinesszz.MySetter”})
支持的注解类型
@SupportedSourceVersion(SourceVersion.RELEASE_8)支持的源码类型
2.2Element体系roundEnv.getElementsAnnotatedWith(MyData.class)可以获取被该注解修饰的类或者字段或者方法。下面我们看下Element的类型。
publicclassUser{//TypeElementprivateStringname;//VariableElementprivateIntergeage;//VariableElementpublicStringgetName(){//ExecutableElementreturnthis.name;}publicvoidsetName(//ExecutableElementStringname//VariableElement){this.name=name;}}
如何知道Element的类型呢。
Element#getKind2.2.1获取字段信息这里我们先自定义一个字段类型来获取基础信息,来学习Element
publicclassFieldElementextendsModifierElement{/*字段名*/privatefinalStringfieldName;/字段类型/privateClass<?>fieldType;/*资源原始类型*/privatefinalVariableElementfieldElement;/基本类型提示/privateStringremark;/*字段所属类*/privatefinalTypeElementclassElement;publicFieldElement(StringfieldName,VariableElementfieldElement){super(fieldElement);this.fieldName=fieldName;this.fieldElement=fieldElement;this.classElement=(TypeElement)fieldElement.getEnclosingElement();try{if(isPrimitive()){fieldType=null;this.remark=“基本类型:”+fieldElement.asType().toString();}else{this.fieldType=Class.forName(fieldElement.asType().toString());}}catch(ClassNotFoundExceptione){//如果还报错说明是一个泛型根据泛型类型来进行处理fieldElement.asType()//DeclaredTypeSet<String>//WildcardType//?//?extendsNumber//?superTthis.fieldType=Object.class;}}}首先先判断是字段类型 publicstaticFieldElementtoFiledElement(ElementenclosedElement){if(ElementKind.FIELD.equals(enclosedElement.getKind())){VariableElementfieldElement=(VariableElement)enclosedElement;NamesimpleName=fieldElement.getSimpleName();returnnewFieldElement(simpleName.toString(),fieldElement);}else{thrownewRuntimeException(“enclosedElement不是字段类型:”+enclosedElement);}}2.2.1获取方法信息方法包括方法参数和返回值,这里我们自定义一个方法参数。 publicclassMethodElementextendsModifierElement{/ publicstaticMethodParamElementtoMethodParamElement(ElementenclosedElement){if(ElementKind.PARAMETER.equals(enclosedElement.getKind())){VariableElementfieldElement=(VariableElement)enclosedElement;NamesimpleName=fieldElement.getSimpleName();returnnewMethodParamElement(simpleName.toString(),fieldElement);}else{thrownewRuntimeException(“enclosedElement不是字段类型:”+enclosedElement);}}publicstaticMethodElementtoMethodElement(ElementenclosedElement){if(ElementKind.METHOD.equals(enclosedElement.getKind())){ExecutableElementmethodElement=(ExecutableElement)enclosedElement;List<?extendsVariableElement>parameters=methodElement.getParameters();List<MethodParamElement>paramElements=newArrayList<>();for(VariableElementparameter:parameters){paramElements.add(toMethodParamElement(parameter));}returnnewMethodElement(methodElement,paramElements);}else{thrownewRuntimeException(“enclosedElement不是方法类型:”+enclosedElement.getClass());}}2.2.2获取类信息 类信息包括字段和方法 publicclassClassElementextendsModifierElement{/
生成类信息
publicstaticClassElementtoClassElement(ElementenclosedElement){if(ElementKind.CLASS.equals(enclosedElement.getKind())){List<?extendsElement>enclosedElements=enclosedElement.getEnclosedElements();List<FieldElement>fieldElements=newArrayList<>();List<MethodElement>methodElements=newArrayList<>();for(Elementelement:enclosedElements){if(ElementKind.FIELD.equals(element.getKind())){fieldElements.add(toFiledElement(element));}if(ElementKind.METHOD.equals(element.getKind())){methodElements.add(toMethodElement(element));}}returnnewClassElement(enclosedElement,fieldElements,methodElements);}else{thrownewRuntimeException(“enclosedElement不是字段类型:”+enclosedElement);}}2.3日志打印
APT方法中日志的打印,要使用工具。在初始化方法中获取消息打印实例。
publicclassMyLombokProcessorextendsAbstractProcessor{privateMessagermessage;@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnvironment){super.init(processingEnvironment);message=processingEnvironment.getMessager();}@Overridepublicbooleanprocess(Set<?extendsTypeElement>set,RoundEnvironmentroundEnv){//扫描所有被@MyData注解的元素processingEnv.getMessager().printMessage(NOTE,“————MyData———–”+roundEnv.getElementsAnnotatedWith(MyData.class));}}
就像log日志一样,他也是有消息类型的,如:提示、异常、警告等。如下枚举
/*诊断类型,例如错误或警告。诊断的类型可用于确定应如何将诊断呈现给用户。例如,错误可能被涂成红色或以“错误”一词为前缀,*而警告可能被涂成黄色或以“警告”一词为前缀。没有要求Kind应该对诊断消息暗示任何固有的语义含义:例如,一个工具可能会提供一个选项来将所有警告报告为错误。/enumKind{/阻止工具正常完成编译/ERROR,/*警告*/WARNING,/类似于警告的问题,但由工具规范强制要求。例如,Java?语言规范要求对某些未经检查的操作和使用过时的方法发出警告。/MANDATORY_WARNING,/*来自该工具的信息性消息。*/NOTE,/其他类型的诊断/OTHER,}2.4字节码修改
字节码修改首先我们要拿到字节码语法树对象,通过观察者模式类进行修改。这里也在初始化时候获取工具。如下我们先定义一个工具。
publicclassClassElementBuilder{privateProcessingEnvironmentprocessingEnv;privateJavacTreestrees;protectedNamesnames;protectedTreeMakertreeMaker;publicClassElementBuilder(ProcessingEnvironmentprocessingEnv){Contextcontext=((JavacProcessingEnvironment)processingEnv).getContext();this.processingEnv=processingEnv;this.trees=JavacTrees.instance(processingEnv);this.treeMaker=TreeMaker.instance(context);this.names=Names.instance(context);}}
处理器初始化方法进行工具的实例化。
@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnvironment){super.init(processingEnvironment);this.classElementBuilder=newClassElementBuilder(processingEnvironment);}
此时我们就能对添加和修改语法树了。但是这里我们先不着急,我们在先学习一下语法树的API。
2.5JCTree语法树2.5.1定义字段定义变量使用
TreeMaker#VarDef(JCTree.JCModifiers字段修饰符,Names字段名,JCExpression字段类型,JCExpression赋值语句)privateString\({fieldName};privateJCTree.JCVariableDeclgenerateStringField(JCTree.JCClassDecljcClassDecl,StringfieldName){JCTree.JCVariableDeclvar=treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString(fieldName),treeMaker.Ident(names.fromString("String")),null);jcClassDecl.defs=jcClassDecl.defs.prepend(var);returnvar;}privateString\){fieldName}=\({fieldName}privateJCTree.JCVariableDeclgenerateStringField(JCTree.JCClassDecljcClassDecl,StringfieldName){//字段的赋值语句JCTree.JCVariableDeclvar=treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString(fieldName),treeMaker.Ident(names.fromString("String")),treeMaker.Literal(fieldName));jcClassDecl.defs=jcClassDecl.defs.prepend(var);returnvar;}<p>要想理解这个API,实现要分析字段是由什么构成的,正如下图。</p><p>标示符三种处理方式。</p>包装类型,不用引入包,可以直接使用TreeMaker#IdentJCExpression<p>treeMaker.Ident(names.fromString("String"))基本类型,不用引入包,可以直接使用TreeMaker#TypeIdent<p>treeMaker.TypeIdent(TypeTag.INT)引用类型,需要引入包后再直接使用先引入包,然后就向包装类型那样进行处理。<p>//importpackageprivateJCTree.JCImportgenImportPkg(StringpackageName,StringclassName){JCTree.JCIdentident=treeMaker.Ident(names.fromString(packageName));returntreeMaker.Import(treeMaker.Select(ident,names.fromString(className)),false);}2.5.2定义get和set方法<p>生成set方法,方法是由</p>方法修饰符treeMaker.Modifiers(Flags.PUBLIC+Flags.STATIC+Flags.FINAL)方法名names.fromString("setName")方法返回值treeMaker#Type、treeMaker#TypeIdent<p>/***publicvoidsetName(Stringname){*this.name=name;*}**@paramjcClassDecl类*@paramf字段*@paramfieldName字段名*/privatevoidgenerateSetMethod(JCTree.JCClassDecljcClassDecl,JCTree.JCVariableDeclf,StringfieldName){//方法体内容ListBuffer<JCTree.JCStatement>statements=newListBuffer<>();//this.MyDateJCTree.JCFieldAccessaThis=treeMaker.Select(treeMaker.Ident(names.fromString("this")),names.fromString(fieldName));//this.MyDate=MyDate;JCTree.JCExpressionStatementexec=treeMaker.Exec(treeMaker.Assign(aThis,treeMaker.Ident(names.fromString(fieldName))));statements.add(exec);JCTree.JCBlockbody=treeMaker.Block(0,statements.toList());//方法参数JCTree.JCVariableDeclparam=treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),names.fromString(fieldName),f.vartype,null);com.sun.tools.javac.util.List<JCTree.JCVariableDecl>parameters=com.sun.tools.javac.util.List.of(param);JCTree.JCMethodDeclgetNameMethod=treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),//方法修饰符names.fromString("set"+capRename(fieldName)),//方法名,capName转驼峰treeMaker.Type(newType.JCVoidType()),//方法返回值类型List.nil(),parameters,//方法参数List.nil(),body,//方法体null);//插入到语法树中jcClassDecl.defs=jcClassDecl.defs.prepend(getNameMethod);}/***publicvoidgetName(){*returnthis.name;*}**@paramjcClassDecl类*@paramf字段*@paramfieldName字段名*/privatevoidgenerateGetMethod(JCTree.JCClassDecljcClassDecl,JCTree.JCVariableDeclf,StringfieldName){//方法体内容ListBuffer<JCTree.JCStatement>statements=newListBuffer<>();//this.nameJCTree.JCFieldAccessselect=treeMaker.Select(treeMaker.Ident(names.fromString("this")),names.fromString(fieldName));//生成return代码returnthis.nameJCTree.JCReturnjcReturn=treeMaker.Return(select);statements.add(jcReturn);//方法体JCTree.JCBlockbody=treeMaker.Block(0,statements.toList());//生成方法JCTree.JCMethodDeclgetNameMethod=treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),//方法修饰符names.fromString("get"+capRename(fieldName)),//方法名f.vartype,//方法返回值类型List.nil(),List.nil(),//方法参数List.nil(),body,//方法体null);//插入到语法树中jcClassDecl.defs=jcClassDecl.defs.prepend(getNameMethod);}<p>参考文章</p>2.6TreeMaker<p>部分内容翻译自</p>2.6.1Modifiers修饰符<p>java中类,方法,字段都是有修饰符的。</p><p>publicJCModifiersModifiers(longflags){returnModifiers(flags,List.<JCAnnotation>nil());}publicJCModifiersModifiers(longflags,List<JCAnnotation>annotations){JCModifierstree=newJCModifiers(flags,annotations);booleannoFlags=(flags&(Flags.ModifierFlags|Flags.ANNOTATION))==0;tree.pos=(noFlags&&annotations.isEmpty())?Position.NOPOS:pos;returntree;}定义多个修饰符<p>treeMaker.Modifiers(Flags.PUBLIC+Flags.STATIC+Flags.FINAL);</p>2.6.2ClassDef定义类<p>publicJCClassDeclClassDef(JCModifiersmods,//访问标志,可以通过TreeMaker.Modifiers来创建Namename,//类名List<JCTypeParameter>typarams,//泛型参数列表JCExpressionextending,//父类List<JCExpression>implementing,//实现的接口List<JCTree>defs){//类定义的详细语句,包括字段、方法的定义等等JCClassDecltree=newJCClassDecl(mods,name,typarams,extending,implementing,defs,null);tree.pos=pos;returntree;}2.6.3MethodDef定义方法<p>publicJCMethodDeclMethodDef(JCModifiersmods,//mods:访问标志Namename,//方法名JCExpressionrestype,//返回类型,返回类型restype填写null或者treeMaker.TypeIdent(TypeTag.VOID)都代表返回void类型List<JCTypeParameter>typarams,//泛型参数列表List<JCVariableDecl>params,//参数列表List<JCExpression>thrown,//异常声明列表JCBlockbody,//方法体JCExpressiondefaultValue//默认方法(可能是interface中的哪个default)){JCMethodDecltree=newJCMethodDecl(mods,name,restype,typarams,params,thrown,body,defaultValue,null);tree.pos=pos;returntree;}2.6.4VarDef定义字段<p>publicJCVariableDeclVarDef(JCModifiersmods,//访问标志Namename,//参数名称JCExpressionvartype,//类型JCExpressioninit//初始化语句){JCVariableDecltree=newJCVariableDecl(mods,name,vartype,init,null);tree.pos=pos;returntree;}publicJCVariableDeclVarDef(VarSymbolv,JCExpressioninit){return(JCVariableDecl)newJCVariableDecl(Modifiers(v.flags(),Annotations(v.getAnnotationMirrors())),v.name,Type(v.type),init,v).setPos(pos).setType(v.type);}2.6.5Return定义返回<p>publicJCReturnReturn(JCExpressionexpr){JCReturntree=newJCReturn(expr);tree.pos=pos;returntree;}2.6.6Ident定义关键字treeMaker.Ident(names.fromString("this"))<p>publicJCIdentIdent(Namename){JCIdenttree=newJCIdent(name,null);tree.pos=pos;returntree;}publicJCIdentIdent(Symbolsym){return(JCIdent)newJCIdent((sym.name!=names.empty)?sym.name:sym.flatName(),sym).setPos(pos).setType(sym.type);}publicJCExpressionIdent(JCVariableDeclparam){returnIdent(param.sym);}2.6.7Select<p>TreeMaker.Select用于创建域访问/方法访问</p><p>publicJCFieldAccessSelect(JCExpressionselected,Nameselector){JCFieldAccesstree=newJCFieldAccess(selected,selector,null);tree.pos=pos;returntree;}publicJCExpressionSelect(JCExpressionbase,Symbolsym){returnnewJCFieldAccess(base,sym.name,sym).setPos(pos).setType(sym.type);}<p>两个参数第一个是对象,第二个是对象的那个方法。</p>this.\){fieldName}
JCTree.JCFieldAccessaThis=treeMaker.Select(treeMaker.Ident(names.fromString(“this”)),names.fromString(fieldName));2.6.8NewClass
用于创建new语句语法树节点(JCNewClass)
publicJCNewClassNewClass(JCExpressionencl,List<JCExpression>typeargs,//参数类型列表JCExpressionclazz,//待创建对象的类型List<JCExpression>args,//参数列表JCClassDecldef){//类定义JCNewClasstree=newJCNewClass(encl,typeargs,clazz,args,def);tree.pos=pos;returntree;}2.6.9Assign赋值语句
类似Select,如果说Select是.那么Assign是=
publicJCAssignAssign(JCExpressionlhs,JCExpressionrhs){JCAssigntree=newJCAssign(lhs,rhs);tree.pos=pos;returntree;}
select和assign配合使用
//this.MyDateJCTree.JCFieldAccessaThis=treeMaker.Select(treeMaker.Ident(names.fromString(“this”)),names.fromString(fieldName));//this.MyDate=MyDate;JCTree.JCExpressionStatementexec=treeMaker.Exec(treeMaker.Assign(aThis,treeMaker.Ident(names.fromString(fieldName))));2.6.10Exec执行语句
配合赋值语句使用
publicJCExpressionStatementExec(JCExpressionexpr){JCExpressionStatementtree=newJCExpressionStatement(expr);tree.pos=pos;returntree;}2.6.11Apply方法调用
//创建一个方法调用user.say(“helloworld!”);JCTree.JCExpressionStatementexec2=treeMaker.Exec(treeMaker.Apply(com.sun.tools.javac.util.List.nil(),treeMaker.Select(treeMaker.Ident(names.fromString(“user”)),//.左边的内容names.fromString(“say”)//.右边的内容),com.sun.tools.javac.util.List.of(treeMaker.Literal(“helloworld!”))//方法中的内容));2.7代码片段
2.6的API,是真的难用,这里列几个片段。
2.7.1生成字段privateStringage;//生成参数例如:privateStringage;treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString(“age”),treeMaker.Ident(names.fromString(“String”)),null);2.7.2赋值变量privateStringname=“西魏陶渊明”
treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString(“name”),treeMaker.Ident(names.fromString(“String”)),treeMaker.Literal(“西魏陶渊明”))2.7.3相加JCTree.Tag.PLUS
treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString(“add”)),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal(“a”),treeMaker.Literal(“b”))))2.7.4+=语法JCTree.Tag.PLUS_ASG
treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG,treeMaker.Ident(names.fromString(“add”)),treeMaker.Literal(“test”)))2.7.5++语法
treeMaker.Exec(treeMaker.Unary(JCTree.Tag.PREINC,treeMaker.Ident(names.fromString(“i”))))2.7.6If语句
/创建一个if语句if(“BuXueWuShu”.equals(name)){add=“a”+“b”;}else{add+=“test”;}///“BuXueWuShu”.equals(name)JCTree.JCMethodInvocationapply=treeMaker.Apply(com.sun.tools.javac.util.List.nil(),treeMaker.Select(treeMaker.Literal(“BuXueWuShu”),//.左边的内容names.fromString(“equals”)//.右边的内容),com.sun.tools.javac.util.List.of(treeMaker.Ident(names.fromString(“name”))));//add=“a”+“b”JCTree.JCExpressionStatementexec3=treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString(“add”)),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal(“a”),treeMaker.Literal(“b”))));//add+=“test”JCTree.JCExpressionStatementexec1=treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG,treeMaker.Ident(names.fromString(“add”)),treeMaker.Literal(“test”)));JCTree.JCIfanIf=treeMaker.If(apply,//if语句里面的判断语句exec3,//条件成立的语句exec1//条件不成立的语句);