在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言简介 Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它。Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现Lombok,开发人员可以节省构建诸如hashCode()和equals()这样的方法以及以往用来分类各种accessor和mutator的大量时间。 Lombok的实现方式是什么呢? import lombok.Getter; import lombok.Setter; @Getter @Setter public class UserInfo { private String userId; private String userName; } 打开编译后生成的UserInfo.class文件 发现已经生成了get、set方法,由此可以推断出Lombok是在编译期为代码进行了增强,那么在编译期进行增强是如何实现的? 编译阶段在JDK6提出并通过了JSR-269提案,提案通过了一组被称为“插入式注解处理器”的标准API,可以提前至编译期对代码中的特定注解进行处理, 从而影响到编译器的工作过程。
综上所述想实现Lombok的效果只需要遵守JSR-269在编译期对AST进行操作即可实现 实现JCTreeJCTree是AST元素的基类,想实现效果只需要添加JCTree节点即可 从类名可以猜到是什么节点使用的这里挑几个常用的解释 TreeMaker主要用于生成语法树节点 代码首先需要注解类,标明作用的范围和作用的时期,两个类分别对应Lombok的Getter、Setter @Target({ElementType.TYPE}) //加在类上的注解 @Retention(RetentionPolicy.SOURCE) //作用于编译期 public @interface Getter { } @Target({ElementType.TYPE}) //加在类上的注解 @Retention(RetentionPolicy.SOURCE) //作用于编译期 public @interface Setter { } 新建抽象注解处理器需要继承AbstractProcessor,这里使用模板方法模式 public abstract class MyAbstractProcessor extends AbstractProcessor { //语法树 protected JavacTrees trees; //构建语法树节点 protected TreeMaker treeMaker; //创建标识符的对象 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) { //获取注解标识的类 Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(getAnnotation()); //拿到语法树 set.stream().map(element -> trees.getTree(element)).forEach(jcTree -> jcTree.accept(new TreeTranslator() { //拿到类定义 @Override public void visitClassDef(JCTree.JCClassDecl jcClassDecl) { List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil(); //拿到所有成员变量 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; } /** * 创建方法 * @param jcVariableDecl * @return */ public abstract JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl); /** * 获取何种注解 * @return */ public abstract Class<? extends Annotation> getAnnotation(); } 用于处理Setter注解 继承MyAbstractProcessor @SupportedAnnotationTypes("com.ingxx.processor.Setter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes @SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion public class SetterProcessor extends MyAbstractProcessor { @Override public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) { ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>(); //生成函数体 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()); //生成方法 return treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), //访问标志 getNewMethodName(jcVariableDecl.getName()), //名字 treeMaker.TypeIdent(TypeTag.VOID), //返回类型 List.nil(), //泛型形参列表 List.of(getParameters(jcVariableDecl)), //参数列表 List.nil(), //异常列表 body, //方法体 null //默认方法(可能是interface中的那个default) ); } @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), //访问标志 prototypeJCVariable.name, //名字 prototypeJCVariable.vartype, //类型 null //初始化语句 ); } } 用于处理Getter注解 继承MyAbstractProcessor @SupportedAnnotationTypes("com.ingxx.processor.Getter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes @SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion public class GetterProcessor extends MyAbstractProcessor { @Override public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) { ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>(); //生成函数体 return this.字段名 statements.append(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), 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())); } } 编译现有类 javac -cp $JAVA_HOME/lib/tools.jar *.java -d . 新建一个测试类 IDEA中由于找不到get set方法会报错,可以忽略 @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("得物"); System.out.println("id = "+userInfo.getUserId()+" name = "+userInfo.getUserName()); } } 接着编译 //多个处理器用逗号分隔 javac -processor com.ingxx.processor.GetterProcessor,com.ingxx.processor.SetterProcessor UserInfo.java -d . 查看编译后的文件发现已经生成了get、set方法 以上就是从Lombok到JSR-269的详细内容,更多关于JS 反射机制的资料请关注极客世界其它相关文章! |
请发表评论