-
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
-
}
定义注解处理器
我们要定义注解处理器的话,那么就需要继承 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");
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
。**
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-P8MdkO6Z-1715826338409)]
[外链图片转存中…(img-gQwxdbaJ-1715826338409)]
[外链图片转存中…(img-Ty8ygAzP-1715826338409)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!