lombok注解实现(annotation)

一、注解

注解相关内容,可自行百度。lombok中注解,比如setter、getter等,实现是怎么样呢?本质是通过语法树,修改源码的方式。这里自己实现了一个简单注解:主要实现在方法入口、退出打印。

二、idea创建工程

通过idea创建一个工程,并且添加两个module,如下图所示:

2.1、ztrace工程

2.1.1、pom文件

<?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.study.trace</groupId>
    <artifactId>ztrace</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 用于调试processor代码 -->
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0-rc7</version>
        </dependency>
        <!-- 编译有用 -->
        <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.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <!-- 编译顺序 先编译processor-->
                    <execution>
                        <id>default-compile</id>
                        <configuration>
                            <compilerArgument>-proc:none</compilerArgument>
                            <includes>    
                           <include>com/study/ztrace/ZTraceProcessor.java</include>
                            </includes>
                        </configuration>
                    </execution>
                    <!-- 编译其他java文件 -->
                    <execution>
                        <id>compile-project</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.1.2、源码代码

//ZTrace.java

package com.study.ztrace;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ZTrace {
}
//ZTraceProcessor.java

package com.study.ztrace;


import com.google.auto.service.AutoService;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;


@SupportedAnnotationTypes({"com.study.ztrace.ZTrace"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class ZTraceProcessor extends AbstractProcessor {

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        final Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        final JavacElements elementUtils = (JavacElements) processingEnv.getElementUtils();
        final TreeMaker treeMaker = TreeMaker.instance(context);
        Set<? extends Element> elements = roundEnv.getRootElements();

        for (Element element : roundEnv.getElementsAnnotatedWith(ZTrace.class)) {
            JCTree.JCMethodDecl jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);
            treeMaker.pos = jcMethodDecl.pos;
            jcMethodDecl.body = treeMaker.Block(0, List.of(
                    treeMaker.Exec(
                            treeMaker.Apply(
                                    List.<JCTree.JCExpression>nil(),
                                    treeMaker.Select(
                                            treeMaker.Ident(
                                                    elementUtils.getName("logger")
                                            ),
                                            elementUtils.getName("info")
                                    ),
                                    List.<JCTree.JCExpression>of(
                                            treeMaker.Literal(jcMethodDecl.getName().toString() + " {") //method name
                                    )
                            )
                    ),
                    jcMethodDecl.body,
                    treeMaker.Exec(
                            treeMaker.Apply(
                                    List.<JCTree.JCExpression>nil(),
                                    treeMaker.Select(
                                            treeMaker.Ident(
                                                    elementUtils.getName("logger")
                                            ),
                                            elementUtils.getName("info")
                                    ),
                                    List.<JCTree.JCExpression>of(
                                            treeMaker.Literal("}")
                                    )
                            )
                    )
            ));

        }
        return false;
    }
}

2.1.3、resources文件

在resources目下创建META-INF/services/javax.annotation.processing.Processor文件,文件内容如下:

com.study.ztrace.ZTraceProcessor

2.1.4、编译

我们可以在通过命令行进行,mvn clean install 即可

2.2、tracedemo工程

2.2.1、pom文件

<?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.study.trace</groupId>
    <artifactId>tracedemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>com.study.trace</groupId>
            <artifactId>ztrace</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

2.2.2、源码代码

//Main.java

package com.tracedemo;

import com.study.ztrace.ZTrace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    @ZTrace
    public void add(int a, int b) {
        int r = a + b;
        System.out.println("a+b=" + r); //这里只有一个打印啊
    }

    public static void main(String[] args) throws Exception{
        new Main().add(512, 512);
    }
}

2.3.3、resources文件

在resources目录下面创建log4j.properties配置文件,用于配置log

### set log levels ###
log4j.rootLogger = debug,stdout,D,E

### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern =  %-d{yyyy-MM-dd HH:mm:ss} %p [%c] %m%n

### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n


### logger ###
# 设置后com.xiaofan路径下的日志将只输出ERROR日志
#log4j.logger.com.xiaofan=ERROR

2.2.4、编译

通过idea直接运行即可,正常可以输出如下内容:

2020-11-26 16:47:21 INFO [com.tracedemo.Main] add {
a+b=1024
2020-11-26 16:47:21 INFO [com.tracedemo.Main] }

其中:log info内容是通过ZTrace注解实现的,目前写的ZTrace只能在void类型方法使用。

三、调试AbstractProcessor

继承AbstractProcessor的类,不能直接调试(在编译期,非运行期),网上有很多方式,但是我只成功autoservice这种方式。这里把调试步骤详细说明一下(以上代码已经写好):

3.1、引用autosevice的jar包

<dependency>
     <groupId>com.google.auto.service</groupId>
     <artifactId>auto-service</artifactId>
     <version>1.0-rc7</version>
</dependency>

3.2、在AbstactProcessor的实现类中添加AutoService注解

@SupportedAnnotationTypes({"com.study.ztrace.ZTrace"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class ZTraceProcessor extends AbstractProcessor {
   
   

以下环境配置,都是在tracedemo工程中设置(即使用方进行)

3.3、设置idea远程调试模式,如下图:

3.4、开启Annotation Processor

 3.4、运行

在命令行中执行mvnDebug

然后在idea设置断点并执行debug模式,效果如下:

四、总结

之前对lombok注解功能感觉太神秘了,所以自己花了一些时间研究了一下,揭开这个面纱,当了解后发现我们还可以去做更多东西。练习过程中参考了,如下内容博客

https://www.cnblogs.com/throwable/p/9139908.html

https://nicky-chen.github.io/2019/05/03/apt_lombok_implement/

https://blog.csdn.net/dap769815768/article/details/90448451

https://blog.csdn.net/vector_M/article/details/105037394

https://blog.mythsman.com/post/5d2c11c767f841464434a3bf/

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/110134655