「lombok原理」无聊的周末一个人手写一个lombok

一、什么是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{/方法参数名/privatefinalStringmethodName;/*返回值*/privateClass<?>returnType;/方法原始信息/privatefinalExecutableElementmethodElement;/*方法参数*/privatefinalList<MethodParamElement>methodParamElements;publicMethodElement(ExecutableElementmethodElement,List<MethodParamElement>methodParamElements){super(methodElement);this.methodName=methodElement.getSimpleName().toString();try{TypeMirrorreturnTypeMirror=methodElement.getReturnType();if(returnTypeMirrorinstanceofNoType){this.returnType=Void.TYPE;}else{this.returnType=Class.forName(methodElement.getReturnType().toString());}}catch(ClassNotFoundExceptione){this.returnType=Void.TYPE;}this.methodElement=methodElement;this.methodParamElements=methodParamElements;}}生成方法

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{/类名称/privatefinalStringclassName;/*包名称*/privatefinalStringpackageName;/类原始信息/privatefinalTypeElementclassElement;/*字段信息*/privatefinalList<FieldElement>fieldElements;/方法信息/privatefinalList<MethodElement>methodElements;publicClassElement(ElementenclosedElement,List<FieldElement>fieldElements,List<MethodElement>methodElements){super(enclosedElement);this.classElement=(TypeElement)enclosedElement;this.fieldElements=fieldElements;this.methodElements=methodElements;this.className=classElement.getSimpleName().toString();this.packageName=classElement.getQualifiedName().toString().replaceAll(“\.”+classElement.getSimpleName().toString(),“”);}}

生成类信息

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(&#34;String&#34;)),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(&#34;String&#34;)),treeMaker.Literal(fieldName));jcClassDecl.defs=jcClassDecl.defs.prepend(var);returnvar;}<p>要想理解这个API,实现要分析字段是由什么构成的,正如下图。</p><p>标示符三种处理方式。</p>包装类型,不用引入包,可以直接使用TreeMaker#IdentJCExpression<p>treeMaker.Ident(names.fromString(&#34;String&#34;))基本类型,不用引入包,可以直接使用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(&#34;setName&#34;)方法返回值treeMaker#Type、treeMaker#TypeIdent<p>/***publicvoidsetName(Stringname){*this.name=name;*}**@paramjcClassDecl类*@paramf字段*@paramfieldName字段名*/privatevoidgenerateSetMethod(JCTree.JCClassDecljcClassDecl,JCTree.JCVariableDeclf,StringfieldName){//方法体内容ListBuffer&lt;JCTree.JCStatement&gt;statements=newListBuffer&lt;&gt;();//this.MyDateJCTree.JCFieldAccessaThis=treeMaker.Select(treeMaker.Ident(names.fromString(&#34;this&#34;)),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&lt;JCTree.JCVariableDecl&gt;parameters=com.sun.tools.javac.util.List.of(param);JCTree.JCMethodDeclgetNameMethod=treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),//方法修饰符names.fromString(&#34;set&#34;+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&lt;JCTree.JCStatement&gt;statements=newListBuffer&lt;&gt;();//this.nameJCTree.JCFieldAccessselect=treeMaker.Select(treeMaker.Ident(names.fromString(&#34;this&#34;)),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(&#34;get&#34;+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.&lt;JCAnnotation&gt;nil());}publicJCModifiersModifiers(longflags,List&lt;JCAnnotation&gt;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&lt;JCTypeParameter&gt;typarams,//泛型参数列表JCExpressionextending,//父类List&lt;JCExpression&gt;implementing,//实现的接口List&lt;JCTree&gt;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&lt;JCTypeParameter&gt;typarams,//泛型参数列表List&lt;JCVariableDecl&gt;params,//参数列表List&lt;JCExpression&gt;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(&#34;this&#34;))<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&lt;JCExpression&gt;typeargs,//参数类型列表JCExpressionclazz,//待创建对象的类型List&lt;JCExpression&gt;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//条件不成立的语句);

本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。