Java之Lombok工具使用及编译时注解的一个运行原理

在这里插入图片描述


前言

各位开发人员应该在项目中都使用过Lombok工具,当然没有使用过也没有关系,今天这篇文章会带大家简简单单梳理一遍,没有使用的可以学会使用,使用过的会明白为什么能够实现自动补全代码。


一、很多人会问:Why use Lombok?

Alt
答:Because 能够简化我们许多的代码,但是该有的功能一点也不少。

二、那么Lombok到底是个什么呢?

简单的来说Lombok是通过一个简单的注解形式可以来帮助我们简化一些 Java 代码的工具。

比如我们新建了一个类,然后定义几个类属性字段,正常情况下我们需要手动去建立getter和setter方法啊,构造函数啊之类的,但是如果使用Lombok的话,就会为我们省去我们手动创建这些代码的麻烦,它能够在我们编译源码的时候自动帮我们生成这些方法。

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

三、Lombok如何使用呢?

Lombok是一个开源项目,代码是在Lombok中

以下以Maven导入:

<dependency>
		  <groupId>org.projectlombok</groupId>
		  <artifactId>lombok</artifactId>
		  <version>${自定义版本号}</version>
		  <scope>provided</scope>
	  </dependency>

下载Lombok插件支持:
点击File–>Settings,在plugins处搜寻Lombok下载,下载成功后重新启动,结果如下:
在这里插入图片描述
功能
那么Lombok是做什么呢?其实很简单,一个最简单的例子就是能够通过添加注解自动生成一些方法,使我们代码更加简洁易懂。
例如下面一个类。

import lombok.Data;

@Data
public class LombokDemo {
    private String userName;
    private String userId;
    private String age;
    public static void main(String[] args) {
        LombokDemo lombokDemo = new LombokDemo();
        lombokDemo.setAge(18);
        System.out.println("age="+ lombokDemo.getAge());
    } 
}

我们使用Lombok提供的Data注解,在没有写getter、setter方法的时候也能够使用其getXXX、setXXX方法。我们看它编译过后的class文件,可以看到它给我们自动生成了getXXX、setXXX方法。
在这里插入图片描述
当然Lombok的功能不止如此,还有很多自带的其他的注解帮助我们简便开发,网上有许多的关于Lombok的使用方法。一般我们可能会在项目中自定义注解,或者使用Spring框架中@Controller、@Service等等注入Spring管理容器类注解,运行时注解大部分都是通过反射来实现的,所以都是运行时注解。而Lombok是使用编译时注解实现的。

编译时注解是什么呢?

首先我们先了解什么是注解:注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。——————摘自《Thinking in Java》

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

  • 编译期:Java语言的编译期是一段不确定的操作过程,因为它可能是将*.java文件转化成*.class文件的过程;也可能是指将字节码转变成机器码的过程;还可能是直接将*.java编译成本地机器代码的过程

  • 运行期:从JVM加载字节码文件到内存中,到最后使用完毕以后卸载的过程都属于运行期的范畴。

注解原理演示

注解处理工具apt

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

它是javac的一个工具,中文意思为编译时注解处理器。APT可以用来在编译时扫描和处理注解。通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省去了手动编写。
正常情况下使用APT工具只是能够生成一些文件(不仅仅是我们想象的class文件,还包括xml文件等等之类的),并不能修改原有的文件信息。
但是此时估计会有疑问,那么Lombok不就是在我们原有的文件中新增了一些信息吗?
后面会有详细的解释,这里简单介绍一下,其实Lombok是修改了Java中的抽象语法树AST才做到了修改其原有类的信息。
接下来我们演示一下如何用APT工具生成一个class文件,然后我们再说Lombok是如何修改已存在的类中的属性的。

注意: 获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。APT的核心是AbstractProcessor类

定义注解
首先当然我们需要定义自己的注解了

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

@Retention(RetentionPolicy.SOURCE) 
@Target(ElementType.TYPE)
public @interface Demo {
    String value();
}

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

定义注解处理器
我们要定义注解处理器的话,那么就需要继承AbstractProcessor类。

import javax.annotation.processing.AbstractProcessor;
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;
import javax.tools.JavaFileObject;
import java.io.Writer;
import java.util.Set;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.bstek.urule.test.Demo")
public class DemoProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        StringBuilder sb = new StringBuilder();
        sb.append("package com.bstek.urule.test;\n\n")
                .append("public class Test1Demo {\n\n")
                .append("\tpublic String getData() {\n")
                .append("\t\treturn \"");
        for (Element element : roundEnv.getElementsAnnotatedWith(Demo.class)) {
            String objectType = element.getSimpleName().toString();
            sb.append(objectType).append(" hello world!\\n");
        }
        sb.append("\";\n") // end return
                .append("\t}\n") // close method
                .append("}\n"); // close class
        try {
            JavaFileObject source = processingEnv.getFiler().createSourceFile("com.bstek.urule.test.Test1Demo");
            Writer writer = source.openWriter();
            writer.write(sb.toString());
            writer.flush();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

@SupportedSourceVersion(SourceVersion.RELEASE_8)//表示所支持的Java版本
@SupportedAnnotationTypes(“com.bstek.urule.test.Demo”)//表示该
处理器要处理的注解

测试

package com.bstek.urule.test;

@Demo(value = "")
public class TestDemo {
    public static void main(String[] args) {
        System.out.println("test");
    }
}

这样我们在编译期就能生成文件了,接下来演示一下在编译时生成文件,此时不要着急直接进行javac编译,Demo类是注解类没错,而DemorProcessor是注解类的处理器,那么我们在编译TestDemo.Java文件的时候就会触发处理器。因此这两个类是无法一起编译的。

所以我们先将注解类和注解处理器类进行编译,如果分开编译会报错:
在这里插入图片描述

javac src/test/java/com/bstek/urule/test/Demo*

接下来进行编译我们的测试类,此时在编译时需要加上processor参数,用来指定相关的注解处理类。

javac -processor com.bstek.urule.test.DemoProcessor com/bstek/urule/test/TestDemo.java

执行完会报错,但是已经生成成功了,报错是因为IO流错误,不影响
在这里插入图片描述
如下图所示已经生成成功了:
在这里插入图片描述


总结

本文简单的介绍了注解处理器是什么,如何利用注解处理器做一些我们在编译期才能够做的事情,让大家对编译器注解的一个流程有一个大概的了解。喜欢,就点点赞吧

参考链接

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖的肖傲楠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值