zoukankan      html  css  js  c++  java
  • Lombok原理分析及简单实现

    使用

    maven引入依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.14</version>
    </dependency>
    
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
      private String name;
    }
    

    自动生成get,set方法,全参构造器,无参构造器

    public class Client {
      public static void main(String[] args) {
        User user = new User();
        user.setName("lisi");
        System.out.println(user.getName());
      }
    }
    

    原理

    通过网上查资料,lombok的基本流程应该是

    • 定义编译期的注解
    • 利用JSR269 api(Pluggable Annotation Processing API )创建编译期的注解处理器
    • 利用tools.jar的javac api处理AST(抽象语法树)
    • 将功能注册进jar包

    接下来自己实现一个类似功能的Getter注解。

    项目依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.imooc</groupId>
        <artifactId>mylombok</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
    
            <dependency>
                <groupId>com.sun</groupId>
                <artifactId>tools</artifactId>
                <version>1.8</version>
                <scope>system</scope>
                <systemPath>${java.home}/../lib/tools.jar</systemPath>
            </dependency>
    
        </dependencies>
    
        <build>
    
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    主要是tools.jar包的依赖和编译环境的配置。

    创建Getter注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Getter {
    }
    

    注解在编译期起作用。

    创建Getter注解处理器

    @SupportedAnnotationTypes("com.imooc.mylombok.Getter")
    @SupportedSourceVersion(SourceVersion.RELEASE_8)
    public class GetterProcessor extends AbstractProcessor {
     // 打印log
      private Messager messager;
     // 抽象语法树
      private JavacTrees trees;
     // 封装了创建AST节点的一些方法
      private TreeMaker treeMaker;
     // 提供了创建标识符的一些方法
      private Names names;
    
    // 初始化方法
      @Override
      public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
      }
    
    // 真正处理注解的方法
      @Override
      public synchronized boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    // 获取所有包含Getter注解的类
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
        set.forEach(element -> {
          JCTree jcTree = trees.getTree(element);
          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);
                }
              }
    	// 为每一个属性创建get方法
              jcVariableDeclList.forEach(jcVariableDecl -> {
                messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
                jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
              });
              super.visitClassDef(jcClassDecl);
            }
    
          });
        });
    
        return true;
      }
    
    // 创建getter方法
      private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
    
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        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);
      }
    
    // 获取方法名
      private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
      }
    }
    

    打包

    在resources文件夹下创建META-INF文件夹,META-INF下创建services文件夹,services下创建文件名为javax.annotation.processing.Processor的文本文件,内容为com.imooc.mylombok.GetterProcessor。
    添加这个文件是为了将自己添加为processor,方便其他项目调用,但自己编译时又不需要processor,就死循环了,自己在编译时不能添加services文件夹,但又需要打的包里有services文件夹,这时候需要配置maven插件。

    <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <excludes>
                        <exclude>META-INF/**/*</exclude>
                    </excludes>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>2.6</version>
                    <executions>
                        <execution>
                            <id>process-META</id>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>copy-resources</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>target/classes</outputDirectory>
                                <resources>
                                    <resource>
                                        <directory>${basedir}/src/main/resources/</directory>
                                        <includes>
                                            <include>**/*</include>
                                        </includes>
                                    </resource>
                                </resources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    

    执行打包

    mvn clean package
    

    使用Getter注解

    import com.imooc.mylombok.Getter;
    
    @Getter
    public class App {
      private String value;
    
      private String value2;
    
      public App(String value) {
        this.value = value;
      }
    
      public static void main(String[] args) {
        App app = new App("it works");
        System.out.println(app.getValue());
      }
    }
    

    编译App

    javac -cp mylombok-1.0-SNAPSHOT.jar App.java
    

    运行APP

    java App
    

    结果为

    it works
    

    符合预期,说明Getter注解生效了,看一下App的反编译结果

    public class App{
    
        public String getValue2(){
            return value2;
        }
    
        public String getValue(){
            return value;
        }
    
        public App(String s){
            value = s;
        }
    
        public static void main(String args[]){
            App app = new App("it works");
            System.out.println(app.getValue());
        }
    
        private String value;
        private String value2;
    }
    

    确实生成了get方法。

  • 相关阅读:
    K3Wise K3List.OCX引入dotnet问题处理方法
    k3wise dotnet开发老单据时序簿工具条添加按钮
    c#调用dotnet写的com组件碰到注册失败的错误的补救方法
    bat注册dotnet com
    dotnet动态加载以及卸载dll的代码
    c#获取com对象的progid
    jpg和png的区别
    Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingStateLoss()
    Java泛型类型擦除与运行时类型获取
    rxjava介绍
  • 原文地址:https://www.cnblogs.com/strongmore/p/13282763.html
Copyright © 2011-2022 走看看