Java注解之编译时注解

简介: Java注解之编译时注解

Java注解之编译时注解


编译时注解指的是:@Retention(RetentionPolicy.CLASS)的注解

需要继承 AbstractProcessor 实现注解处理器

需要在build.gradle中

implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

并且在使用注解的module中引用注解处理器模块

annotationProcessor project(':anno')

AbstractProcessor

是一个抽象类,实现注解处理器必须继承它,下面主要介绍里面的几个常用方法。

init(ProcessingEnvironment processingEnv)

初始化方法,用于获取一些有用的系统工具类,如Elements, Filer, Messager,Types等;

ProcessingEnvironment

初始方法作为参数传入,源码如下:

public interface ProcessingEnvironment {
    Map<String, String> getOptions();
    Messager getMessager();
    Filer getFiler();
    Elements getElementUtils();
    Types getTypeUtils();
    SourceVersion getSourceVersion();
    Locale getLocale();
}
  • getOptions:用来接收外部自定义参数的,后面getSupportedOptions方法中会介绍使用
  • getMessager:返回一个Messager对象,作为日志打印工具。看到有人说AnnotationProcessor是运行在javac期间,不能用System.out.print打印,其实System.out.print也是可以的,但是processingEnv.getMessager()功能更强大,可以区分Log类型,且Log为 Error类型时,会直接终止程序。

messager = processingEnvironment.getMessager();

messager.printMessage(Diagnostic.Kind.NOTE, "init");

  • 日志类型有:
public static enum Kind {
        ERROR,
        WARNING,
        MANDATORY_WARNING,
        NOTE,
        OTHER;
        private Kind() {
        }
    }

执行Rebuild Project可以在Build窗口看到如下日志


getFiler:返回一个Filer对象,主要负责生成文件。源码如下


public interface Filer {
    JavaFileObject createSourceFile(CharSequence var1, Element... var2) throws IOException;
    JavaFileObject createClassFile(CharSequence var1, Element... var2) throws IOException;
    FileObject createResource(Location var1, CharSequence var2, CharSequence var3, Element... var4) throws IOException;
    FileObject getResource(Location var1, CharSequence var2, CharSequence var3) throws IOException;
}


  • getElementUtils:返回一个Elements对象,和Element相关的工具类。比如我们要获取包名怎么办?可以通过上面介绍过的getEnclosingElement方法一层一层往上找,非常麻烦也很容易出错。还可以通过Elements中的getPackageOf方法直接获取到
  • getTypeUtils:返回一个Types对象,和元素类型相关的工具类
  • getSourceVersion:支持的Java版本
  • getLocale:返回Locale对象,这个没什么可说的,就是国际化的东西
  • getSupportedOptions()

这个方法允许我们自定义一些参数传给Processor,就拿ARouter举例子

@Override
public Set<String> getSupportedOptions() {
    return new HashSet<String>() {{
        this.add(KEY_MODULE_NAME);
        this.add(KEY_GENERATE_DOC_NAME);
    }};
}

然后在gralde文件中的传一个参数

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

最后在Processor的init方法中获取参数

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    ...
    // Attempt to get user configuration [moduleName]
    Map<String, String> options = processingEnv.getOptions();
    if (MapUtils.isNotEmpty(options)) {
        moduleName = options.get(KEY_MODULE_NAME); 
    }
    ...
}

getSupportedSourceVersion()

设置支持的java版本,一般返回最近版本就行。

@Override
public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
}
也可以使用@SupportedSourceVersion注解完成。
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestProcessor extends AbstractProcessor {
    ...
}

getSupportedAnnotationTypes()

设置支持的注解类型,只有在这个方法中添加过的注解才会被注解处理器所处理。

比如你自定义了个@TestAnnotation注解

@Override
public Set<String> getSupportedAnnotationTypes() {
    HashSet<String> set = new HashSet<>();
    set.add(TestAnnotation.class.getCanonicalName());
    return set;
}

也可以用@SupportedAnnotationTypes注解完成

@SupportedAnnotationTypes("com.wzc.annotation.TestAnnotation")
public class TestProcessor extends AbstractProcessor {
    ...
}

process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)

所有关于注解的处理和java文件的生成都是在这个方法中完成,当返回值为true的时候表示这个Processor处理的注解不会再被后续的Processor处理。如果返回false,则表示这些注解还会被后续的Processor处理,类似拦截器模式。

参数

  • TypeElementSet:所有待处理的的注解集合。(一般用不上)
  • roundEnvironment:表示当前注解所处的环境,通过这个参数可以查询到当前这一轮注解处理的信息。主要使用roundEnvironment.getElementsAnnotatedWith(TestAnnotation.class)方法获取我们自定义注解标注的所有元素集合,是个Set<? extends Element>
Element

所有被注解标注的部分都会被解析成element,element既可能是类,也可能是类属性,还可能是方法,这就要看你使用自定义注解注解了那些东西了。获取到element之后我们还需要将element转换成对应的子类。

  • ExecutableElement: 可执行元素,包括类或者接口的方法。
  • PackageElement: 包元素
  • TypeElement:类,接口,或者枚举。
  • VariableElement: 类属性,枚举常量,方法参数,局部变量或者异常参数。
  • TypeParameterElement: 表示一个泛型元素
package com.wzc.gradle.myaptdemo; // PackageElement
// TypeElement
public class TestClass {
    // VariableElement
    private String mVariableElement;
    // ExecutableElement
    public TestClass(String mVariableElement // TypeElement) {
        this.mVariableElement = mVariableElement;
    }
    // ExecutableElement
    public static void main(String[] args //TypeElement) {
    }
}

我们在定义注解的时候可以指定注解的ElementType,这个ElementType和Element是有对应关系的,通过测试可得到下面表格。

注解的ElementType 注解处理器的Element
TYPE TypeElement
FIELD VariableElement
METHOD ExecutableElement
PARAMETER VariableElement
CONSTRUCTOR ExecutableElement
LOCAL_VARIABLE 获取不到
ANNOTATION_TYPE TypeElement
PACKAGE PackageElement
TYPE_PARAMETER TypeParameterElement
TYPE_USE 1对多,取决于使用的位置

拿到对应Element之后,还需要收集Element的相关信息,下面我们介绍几个常用的方法

  • getSimpleName:获取该元素的名字;
  • getModifiers:获取该元素的访问权限,返回一个Set;
  • asType: 获取该元素的类型,比如String会返回java.lang.String,TextView会返回android.widget.TextView;
  • getEnclosingElement:获取父级元素,比如参数的父级是方法,方法的父级是类或者接口;
  • getQualifiedName:获取全限定名,如果是类的话,包含完整的报名路径;

(316条消息) Java注解之编译时注解_java 编译时注解_wuzuchang2023的博客-CSDN博客


目录
相关文章
|
2月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
59 2
|
25天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
86 43
Java学习十六—掌握注解:让编程更简单
|
20天前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
42 14
|
20天前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
29 12
|
1月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
23 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
28天前
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
41 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
13天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
26 0
|
1月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
34 0
java 常用注解大全、注解笔记
|
1月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0