关于java:面试官你天天用-Lombok说说它什么原理我竟然答不上来…

作者:九年义务教育漏网之鱼\
链接:https://juejin.cn/post/684490…

置信大家在我的项目中都应用过Lombok,因为可能简化咱们许多的代码,然而该有的性能一点也不少。那么lombok到底是个什么呢,lombok是一个能够通过简略的注解的模式来帮忙咱们简化打消一些必须有但显得很臃肿的 Java 代码的工具。

简略来说,比方咱们新建了一个类,而后在其中写了几个字段,而后通常状况下咱们须要手动去建设getter和setter办法啊,构造函数啊之类的,lombok的作用就是为了省去咱们手动创立这些代码的麻烦,它可能在咱们编译源码的时候主动帮咱们生成这些办法。

那么Lombok到底是如何做到这些的呢?其实底层就是用到了编译时注解的性能。

Lombok如何应用

Lombok是一个开源我的项目,代码是在lombok中,如果是gradle我的项目的话间接在我的项目中援用如下即可。

compile ("org.projectlombok:lombok:1.16.6")

性能

那么Lombok是做什么呢?其实很简略,一个最简略的例子就是可能通过增加注解主动生成一些办法,使咱们代码更加简洁易懂。例如上面一个类。

@Data
public class TestLombok {
    private String name;
    private Integer age;

    public static void main(String[] args) {
        TestLombok testLombok = new TestLombok();
        testLombok.setAge(12);
        testLombok.setName("zs");
    }
}

咱们应用Lombok提供的Data注解,在没有写get、set办法的时候也可能应用其get、set办法。咱们看它编译过后的class文件,能够看到它给咱们主动生成了get、set办法。

public class TestLombok {
    private String name;
    private Integer age;

    public static void main(String[] args) {
        TestLombok testLombok = new TestLombok();
        testLombok.setAge(12);
        testLombok.setName("zs");
    }

    public TestLombok() {
    }

    public String getName() {
        return this.name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

当然Lombok的性能不止如此,还有很多其余的注解帮忙咱们简便开发,网上有许多的对于Lombok的应用办法,这里就不再啰嗦了。失常状况下咱们在我的项目中自定义注解,或者应用Spring框架中@Controller、@Service等等这类注解都是运行时注解,运行时注解大部分都是通过反射来实现的。而Lombok是应用编译时注解实现的。那么编译时注解是什么呢?

编译时注解

注解(也被成为元数据)为咱们在代码中增加信息提供了一种形式化的办法,使咱们能够在稍后某个时刻十分不便地应用这些数据。 ——————摘自《Thinking in Java》

Java中的注解分为运行时注解编译时注解,运行时注解就是咱们常常应用的在程序运行时通过反射失去咱们注解的信息,而后再做一些操作。而编译时注解是什么呢?就是在程序在编译期间通过注解处理器进行解决。

  • 编译期:Java语言的编译期是一段不确定的操作过程,因为它可能是将*.java文件转化成*.class文件的过程;也可能是指将字节码转变成机器码的过程;还可能是间接将*.java编译成本地机器代码的过程
  • 运行期:从JVM加载字节码文件到内存中,到最初应用结束当前卸载的过程都属于运行期的领域。

注解解决工具apt

注解解决工具apt(Annotation Processing Tool),这是Sun为了帮忙注解的处理过程而提供的工具,apt被设计为操作Java源文件,而不是编译后的类。

它是javac的一个工具,中文意思为编译时注解处理器。APT能够用来在编译时扫描和解决注解。通过APT能够获取到注解和被注解对象的相干信息,在拿到这些信息后咱们能够依据需要来主动的生成一些代码,省去了手动编写。留神,获取注解及生成代码都是在代码编译时候实现的,相比反射在运行时解决注解大大提高了程序性能。APT的外围是AbstractProcessor类。

失常状况下应用APT工具只是可能生成一些文件(不仅仅是咱们设想的class文件,还包含xml文件等等之类的),并不能批改原有的文件信息。

然而此时预计会有疑难,那么Lombok不就是在咱们原有的文件中新增了一些信息吗?我在前面会有具体的解释,这里简略介绍一下,其实Lombok是批改了Java中的形象语法树AST才做到了批改其原有类的信息。

接下来咱们演示一下如何用APT工具生成一个class文件,而后咱们再说Lombok是如何批改已存在的类中的属性的。

定义注解

首先当然咱们须要定义本人的注解了

@Retention(RetentionPolicy.SOURCE) // 注解只在源码中保留
@Target(ElementType.TYPE) // 用于润饰类
public @interface GeneratePrint {

    String value();
}

Retention注解下面有一个属性value,它是RetentionPolicy类型的枚举类,RetentionPolicy枚举类中有三个值。

public enum RetentionPolicy {

    SOURCE,

    CLASS,

    RUNTIME
}
  • SOURCE润饰的注解:润饰的注解,示意注解的信息会被编译器摈弃,不会留在class文件中,注解的信息只会留在源文件中
  • CLASS润饰的注解:示意注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候
  • RUNTIME润饰的注解:示意注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时。所以它可能通过反射调用,所以失常运行时注解都是应用的这个参数

Target注解下面也有个属性value,它是ElementType类型的枚举。是用来润饰此注解作用在哪的。

public enum ElementType {
    TYPE,

    FIELD,

    METHOD,

    PARAMETER,

    CONSTRUCTOR,

    LOCAL_VARIABLE,

    ANNOTATION_TYPE,

    PACKAGE,

    TYPE_PARAMETER,

    TYPE_USE
}

定义注解处理器

举荐一个 Spring Boot 基础教程及实战示例:
https://github.com/javastacks…

咱们要定义注解处理器的话,那么就须要继承AbstractProcessor类。继承完当前根本的框架类型如下

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("aboutjava.annotion.MyGetter")
public class MyGetterProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return true;
    }
}

咱们能够看到在子类中下面有两个注解,注解形容如下

  • @SupportedSourceVersion:示意所反对的Java版本
  • @SupportedAnnotationTypes:示意该处理器要解决的注解

继承了父类的两个办法,办法形容如下

  • init办法:次要是取得编译期间的一些环境信息
  • process办法:在编译时,编译器执行的办法。也就是咱们写具体逻辑的中央

咱们是演示一下如何通过继承AbstractProcessor类来实现在编译时生成类,所以咱们在process办法中书写咱们生成类的代码。如下所示。

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    StringBuilder builder = new StringBuilder()
            .append("package aboutjava.annotion;\n\n")
            .append("public class GeneratedClass {\n\n") // open class
            .append("\tpublic String getMessage() {\n") // open method
            .append("\t\treturn \"");
    // for each javax.lang.model.element.Element annotated with the CustomAnnotation
    for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
        String objectType = element.getSimpleName().toString();
        // this is appending to the return statement
        builder.append(objectType).append(" says hello!\\n");
    }
    builder.append("\";\n") // end return
            .append("\t}\n") // close method
            .append("}\n"); // close class
    try { // write the file
        JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");
        Writer writer = source.openWriter();
        writer.write(builder.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        // Note: calling e.printStackTrace() will print IO errors
        // that occur from the file already existing after its first run, this is normal
    }
    return true;
}

定义应用注解的类(测试类)

下面的两个类就是根本的工具类了,一个是定义了注解,一个是定义了注解处理器,接下来咱们来定义一个测试类(TestAno.java)。咱们在类下面加上咱们自定的注解类。

@MyGetter
public class TestAno {

    public static void main(String[] args) {
        System.out.printf("1");
    }
}

这样咱们在编译期就能生成文件了,接下来演示一下在编译时生成文件,此时不要焦急间接进行javac编译,MyGetter类是注解类没错,而MyGetterProcessor是注解类的处理器,那么咱们在编译TestAnoJava文件的时候就会触发处理器。因而这两个类是无奈一起编译的。

先给大家看一下我的目录构造

aboutjava
    -- annotion
        -- MyGetter.java
        -- MyGetterProcessor.java
        -- TestAno.java

所以咱们先将注解类和注解处理器类进行编译

javac aboutjava/annotion/MyGett*

接下来进行编译咱们的测试类,此时在编译时须要加上processor参数,用来指定相干的注解解决类。

javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java

大家能够看到动态图中,主动生成了Java文件。

近期热文举荐:

1.1,000+ 道 Java面试题及答案整顿(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理