前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >@lombok注解背后的原理是什么,让我们走近自定义Java注解处理器

@lombok注解背后的原理是什么,让我们走近自定义Java注解处理器

作者头像
程序员黄小斜
修改于 2021-11-23 09:38:48
修改于 2021-11-23 09:38:48
8170
举报

本文介绍了如何自定义Java注解处理器及涉及到的相关知识,看完本文可以很轻松看懂并理解各大开源框架的注解处理器的应用。

本文首发:http://yuweiguocn.github.io/

关于自定义Java注解请查看自定义注解

本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发。

基本实现

实现一个自定义注解处理器需要有两个步骤,第一是实现Processor接口处理注解,第二是注册注解处理器。

实现Processor接口

通过实现Processor接口可以自定义注解处理器,这里我们采用更简单的方法通过继承AbstractProcessor类实现自定义注解处理器。实现抽象方法process处理我们想要的功能。

代码语言:txt
AI代码解释
复制
public class CustomProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        return false;
    }
}

除此之外,我们还需要指定支持的注解类型以及支持的Java版本通过重写getSupportedAnnotationTypes方法和getSupportedSourceVersion方法:

代码语言:txt
AI代码解释
复制
public class CustomProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        return false;
    }
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotataions = new LinkedHashSet<String>();
        annotataions.add(CustomAnnotation.class.getCanonicalName());
        return annotataions;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

对于指定支持的注解类型,我们还可以通过注解的方式进行指定:

代码语言:txt
AI代码解释
复制
@SupportedAnnotationTypes({"io.github.yuweiguocn.annotation.CustomAnnotation"})
public class CustomProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        return false;
    }
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

因为Android平台可能会有兼容问题,建议使用重写getSupportedAnnotationTypes方法指定支持的注解类型。

注册注解处理器

最后我们还需要将我们自定义的注解处理器进行注册。新建res文件夹,目录下新建META-INF文件夹,目录下新建services文件夹,目录下新建javax.annotation.processing.Processor文件,然后将我们自定义注解处理器的全类名写到此文件:

代码语言:txt
AI代码解释
复制
io.github.yuweiguocn.processor.CustomProcessor

上面这种注册的方式太麻烦了,谷歌帮我们写了一个注解处理器来生成这个文件。

github地址:https://github.com/google/auto

添加依赖:

代码语言:txt
AI代码解释
复制
compile 'com.google.auto.service:auto-service:1.0-rc2'

添加注解:

代码语言:txt
AI代码解释
复制
@AutoService(Processor.class)
public class CustomProcessor extends AbstractProcessor {
    ...
}

搞定,体会到注解处理器的强大木有。后面我们只需关注注解处理器中的处理逻辑即可。

我们来看一下最终的项目结构:

基本概念

抽象类中还有一个init方法,这是Processor接口中提供的一个方法,当我们编译程序时注解处理器工具会调用此方法并且提供实现ProcessingEnvironment接口的对象作为参数。

代码语言:txt
AI代码解释
复制
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
}

我们可以使用ProcessingEnvironment获取一些实用类以及获取选项参数等:

方法

说明

Elements getElementUtils()

返回实现Elements接口的对象,用于操作元素的工具类。

Filer getFiler()

返回实现Filer接口的对象,用于创建文件、类和辅助文件。

Messager getMessager()

返回实现Messager接口的对象,用于报告错误信息、警告提醒。

Map<String,String> getOptions()

返回指定的参数选项。

Types getTypeUtils()

返回实现Types接口的对象,用于操作类型的工具类。

元素

Element元素是一个接口,表示一个程序元素,比如包、类或者方法。以下元素类型接口全部继承自Element接口:

类型

说明

ExecutableElement

表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素。

PackageElement

表示一个包程序元素。提供对有关包及其成员的信息的访问。

TypeElement

表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意,枚举类型是一种类,而注解类型是一种接口。

TypeParameterElement

表示一般类、接口、方法或构造方法元素的形式类型参数。

VariableElement

表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。

如果我们要判断一个元素的类型,应该使用Element.getKind()方法配合ElementKind枚举类进行判断。尽量避免使用instanceof进行判断,因为比如TypeElement既表示类又表示一个接口,这样判断的结果可能不是你想要的。例如我们判断一个元素是不是一个类:

代码语言:txt
AI代码解释
复制
if (element instanceof TypeElement) { //错误,也有可能是一个接口
}

if (element.getKind() == ElementKind.CLASS) { //正确
    //doSomething
}

下表为ElementKind枚举类中的部分常量,详细信息请查看官方文档。

类型

说明

PACKAGE

一个包。

ENUM

一个枚举类型。

CLASS

没有用更特殊的种类(如 ENUM)描述的类。

ANNOTATION_TYPE

一个注解类型。

INTERFACE

没有用更特殊的种类(如 ANNOTATION_TYPE)描述的接口。

ENUM_CONSTANT

一个枚举常量。

FIELD

没有用更特殊的种类(如 ENUM_CONSTANT)描述的字段。

PARAMETER

方法或构造方法的参数。

LOCAL_VARIABLE

局部变量。

METHOD

一个方法。

CONSTRUCTOR

一个构造方法。

TYPE_PARAMETER

一个类型参数。

类型

TypeMirror是一个接口,表示 Java 编程语言中的类型。这些类型包括基本类型、声明类型(类和接口类型)、数组类型、类型变量和 null 类型。还可以表示通配符类型参数、executable 的签名和返回类型,以及对应于包和关键字 void 的伪类型。以下类型接口全部继承自TypeMirror接口:

类型

说明

ArrayType

表示一个数组类型。多维数组类型被表示为组件类型也是数组类型的数组类型。

DeclaredType

表示某一声明类型,是一个类 (class) 类型或接口 (interface) 类型。这包括参数化的类型(比如 java.util.Set<String>)和原始类型。TypeElement 表示一个类或接口元素,而 DeclaredType 表示一个类或接口类型,后者将成为前者的一种使用(或调用)。

ErrorType

表示无法正常建模的类或接口类型。

ExecutableType

表示 executable 的类型。executable 是一个方法、构造方法或初始化程序。

NoType

在实际类型不适合的地方使用的伪类型。

NullType

表示 null 类型。

PrimitiveType

表示一个基本类型。这些类型包括 boolean、byte、short、int、long、char、float 和 double。

ReferenceType

表示一个引用类型。这些类型包括类和接口类型、数组类型、类型变量和 null 类型。

TypeVariable

表示一个类型变量。

WildcardType

表示通配符类型参数。

同样,如果我们想判断一个TypeMirror的类型,应该使用TypeMirror.getKind()方法配合TypeKind枚举类进行判断。尽量避免使用instanceof进行判断,因为比如DeclaredType既表示类 (class) 类型又表示接口 (interface) 类型,这样判断的结果可能不是你想要的。

TypeKind枚举类中的部分常量,详细信息请查看官方文档。

类型

说明

BOOLEAN

基本类型 boolean。

INT

基本类型 int。

LONG

基本类型 long。

FLOAT

基本类型 float。

DOUBLE

基本类型 double。

VOID

对应于关键字 void 的伪类型。

NULL

null 类型。

ARRAY

数组类型。

PACKAGE

对应于包元素的伪类型。

EXECUTABLE

方法、构造方法或初始化程序。

创建文件

Filer接口支持通过注解处理器创建新文件。可以创建三种文件类型:源文件、类文件和辅助资源文件。

1.创建源文件

代码语言:txt
AI代码解释
复制
JavaFileObject createSourceFile(CharSequence name,
                                Element... originatingElements)
                                throws IOException

创建一个新的源文件,并返回一个对象以允许写入它。文件的名称和路径(相对于源文件的根目录输出位置)基于该文件中声明的类型。如果声明的类型不止一个,则应该使用主要顶层类型的名称(例如,声明为 public 的那个)。还可以创建源文件来保存有关某个包的信息,包括包注解。要为指定包创建源文件,可以用 name 作为包名称,后跟 ".package-info";要为未指定的包创建源文件,可以使用 "package-info"。

2.创建类文件

代码语言:txt
AI代码解释
复制
JavaFileObject createClassFile(CharSequence name,
                               Element... originatingElements)
                               throws IOException

创建一个新的类文件,并返回一个对象以允许写入它。文件的名称和路径(相对于类文件的根目录输出位置)基于将写入的类型名称。还可以创建类文件来保存有关某个包的信息,包括包注解。要为指定包创建类文件,可以用 name 作为包名称,后跟 ".package-info";为未指定的包创建类文件不受支持。

3.创建辅助资源文件

代码语言:txt
AI代码解释
复制
FileObject createResource(JavaFileManager.Location location,
                          CharSequence pkg,
                          CharSequence relativeName,
                          Element... originatingElements)
                          throws IOException

创建一个用于写入操作的新辅助资源文件,并为它返回一个文件对象。该文件可以与新创建的源文件、新创建的二进制文件或者其他受支持的位置一起被查找。位置 CLASS_OUTPUT 和 SOURCE_OUTPUT 必须受支持。资源可以是相对于某个包(该包是源文件和类文件)指定的,并通过相对路径名从中取出。从不太严格的角度说,新文件的完全路径名将是 location、 pkg 和 relativeName 的串联。

对于生成Java文件,还可以使用Square公司的开源类库JavaPoet,感兴趣的同学可以了解下。

打印错误信息

Messager接口提供注解处理器用来报告错误消息、警告和其他通知的方式。

注意:我们应该对在处理过程中可能发生的异常进行捕获,通过Messager接口提供的方法通知用户。此外,使用带有Element参数的方法连接到出错的元素,用户可以直接点击错误信息跳到出错源文件的相应行。如果你在process()中抛出一个异常,那么运行注解处理器的JVM将会崩溃(就像其他Java应用一样),这样用户会从javac中得到一个非常难懂出错信息。

方法

说明

void printMessage(Diagnostic.Kind kind, CharSequence msg)

打印指定种类的消息。

void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e)

在元素的位置上打印指定种类的消息。

void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a)

在已注解元素的注解镜像位置上打印指定种类的消息。

void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v)

在已注解元素的注解镜像内部注解值的位置上打印指定种类的消息。

配置选项参数

我们可以通过getOptions()方法获取选项参数,在gradle文件中配置选项参数值。例如我们配置了一个名为yuweiguoCustomAnnotation的参数值。

代码语言:txt
AI代码解释
复制
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ yuweiguoCustomAnnotation : 'io.github.yuweiguocn.customannotation.MyCustomAnnotation' ]
            }
        }
    }
}

在注解处理器中重写getSupportedOptions方法指定支持的选项参数名称。通过getOptions方法获取选项参数值。

代码语言:txt
AI代码解释
复制
public static final String CUSTOM_ANNOTATION = "yuweiguoCustomAnnotation";

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   try {
       String resultPath = processingEnv.getOptions().get(CUSTOM_ANNOTATION);
       if (resultPath == null) {
           ...
           return false;
       }
       ...
   } catch (Exception e) {
       e.printStackTrace();
       ...
   }
   return true;
}

@Override
public Set<String> getSupportedOptions() {
   Set<String> options = new LinkedHashSet<String>();
   options.add(CUSTOM_ANNOTATION);
   return options;
}

处理过程

Java官方文档给出的注解处理过程的定义:注解处理过程是一个有序的循环过程。在每次循环中,一个处理器可能被要求去处理那些在上一次循环中产生的源文件和类文件中的注解。第一次循环的输入是运行此工具的初始输入。这些初始输入,可以看成是虚拟的第0次的循环的输出。这也就是说我们实现的process方法有可能会被调用多次,因为我们生成的文件也有可能会包含相应的注解。例如,我们的源文件为SourceActivity.class,生成的文件为Generated.class,这样就会有三次循环,第一次输入为SourceActivity.class,输出为Generated.class;第二次输入为Generated.class,输出并没有产生新文件;第三次输入为空,输出为空。

每次循环都会调用process方法,process方法提供了两个参数,第一个是我们请求处理注解类型的集合(也就是我们通过重写getSupportedAnnotationTypes方法所指定的注解类型),第二个是有关当前和上一次 循环的信息的环境。返回值表示这些注解是否由此 Processor 声明,如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们。

代码语言:txt
AI代码解释
复制
public abstract boolean process(Set<? extends TypeElement> annotations,
                                RoundEnvironment roundEnv)

获取注解元素

我们可以通过RoundEnvironment接口获取注解元素。process方法会提供一个实现RoundEnvironment接口的对象。

方法

说明

Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)

返回被指定注解类型注解的元素集合。

Set<? extends Element> getElementsAnnotatedWith(TypeElement a)

返回被指定注解类型注解的元素集合。

processingOver()

如果循环处理完成返回true,否则返回false。

示例

了解完了相关的基本概念,接下来我们来看一个示例,本示例只为演示无实际意义。主要功能为自定义一个注解,此注解只能用在public的方法上,我们通过注解处理器拿到类名和方法名存储到List集合中,然后生成通过参数选项指定的文件,通过此文件可以获取List集合。

自定义注解:

代码语言:txt
AI代码解释
复制
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
}

注解处理器中关键代码:

代码语言:txt
AI代码解释
复制
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   try {
       String resultPath = processingEnv.getOptions().get(CUSTOM_ANNOTATION);
       if (resultPath == null) {
           messager.printMessage(Diagnostic.Kind.ERROR, "No option " + CUSTOM_ANNOTATION +
                   " passed to annotation processor");
           return false;
       }

       round++;
       messager.printMessage(Diagnostic.Kind.NOTE, "round " + round + " process over " + roundEnv.processingOver());
       Iterator<? extends TypeElement> iterator = annotations.iterator();
       while (iterator.hasNext()) {
           messager.printMessage(Diagnostic.Kind.NOTE, "name is " + iterator.next().getSimpleName().toString());
       }

       if (roundEnv.processingOver()) {
           if (!annotations.isEmpty()) {
               messager.printMessage(Diagnostic.Kind.ERROR,
                       "Unexpected processing state: annotations still available after processing over");
               return false;
           }
       }

       if (annotations.isEmpty()) {
           return false;
       }

       for (Element element : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
           if (element.getKind() != ElementKind.METHOD) {
               messager.printMessage(
                       Diagnostic.Kind.ERROR,
                       String.format("Only methods can be annotated with @%s", CustomAnnotation.class.getSimpleName()),
                       element);
               return true; // 退出处理
           }

           if (!element.getModifiers().contains(Modifier.PUBLIC)) {
               messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must be public", element);
               return true;
           }

           ExecutableElement execElement = (ExecutableElement) element;
           TypeElement classElement = (TypeElement) execElement.getEnclosingElement();
           result.add(classElement.getSimpleName().toString() + "#" + execElement.getSimpleName().toString());
       }
       if (!result.isEmpty()) {
           generateFile(resultPath);
       } else {
           messager.printMessage(Diagnostic.Kind.WARNING, "No @CustomAnnotation annotations found");
       }
       result.clear();
   } catch (Exception e) {
       e.printStackTrace();
       messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected error in CustomProcessor: " + e);
   }
   return true;
}

private void generateFile(String path) {
   BufferedWriter writer = null;
   try {
       JavaFileObject sourceFile = filer.createSourceFile(path);
       int period = path.lastIndexOf('.');
       String myPackage = period > 0 ? path.substring(0, period) : null;
       String clazz = path.substring(period + 1);
       writer = new BufferedWriter(sourceFile.openWriter());
       if (myPackage != null) {
           writer.write("package " + myPackage + ";\n\n");
       }
       writer.write("import java.util.ArrayList;\n");
       writer.write("import java.util.List;\n\n");
       writer.write("/** This class is generated by CustomProcessor, do not edit. */\n");
       writer.write("public class " + clazz + " {\n");
       writer.write("    private static final List<String> ANNOTATIONS;\n\n");
       writer.write("    static {\n");
       writer.write("        ANNOTATIONS = new ArrayList<>();\n\n");
       writeMethodLines(writer);
       writer.write("    }\n\n");
       writer.write("    public static List<String> getAnnotations() {\n");
       writer.write("        return ANNOTATIONS;\n");
       writer.write("    }\n\n");
       writer.write("}\n");
   } catch (IOException e) {
       throw new RuntimeException("Could not write source for " + path, e);
   } finally {
       if (writer != null) {
           try {
               writer.close();
           } catch (IOException e) {
               //Silent
           }
       }
   }
}

private void writeMethodLines(BufferedWriter writer) throws IOException {
   for (int i = 0; i < result.size(); i++) {
       writer.write("        ANNOTATIONS.add(\"" + result.get(i) + "\");\n");
   }
}

编译输出:

代码语言:txt
AI代码解释
复制
Note: round 1 process over false
Note: name is CustomAnnotation
Note: round 2 process over false
Note: round 3 process over true

获取完整代码:https://github.com/yuweiguocn/CustomAnnotation

关于上传自定义注解处理器到jcenter中,请查看上传类库到jcenter

很高兴你能阅读到这里,此时再去看EventBus 3.0中的注解处理器的源码,相信你可以很轻松地理解它的原理。

注意:如果你clone了工程代码,你可能会发现注解和注解处理器是单独的module。有一点可以肯定的是我们的注解处理器只需要在编译的时候使用,并不需要打包到APK中。因此为了用户考虑,我们需要将注解处理器分离为单独的module。

参考

作者:于卫国

链接:https://www.jianshu.com/p/50d95fbf635c/

来源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java中的注解处理器是什么,提供一个自定义注解处理器的实际案例
Java中的注解处理器(Annotation Processor)是一种在编译时期处理注解的工具,它可以通过扫描和解析源代码中的注解信息,生成额外的代码、配置文件或者进行其他特定的处理操作。注解处理器能够帮助开发者实现自定义的代码生成、静态分析、验证等功能,从而提高开发效率和代码质量。
用户1289394
2024/06/11
2220
Java中的注解处理器是什么,提供一个自定义注解处理器的实际案例
【Android APT】注解处理器 ( 根据注解生成 Java 代码 )
Android APT 学习进阶路径 : 推荐按照顺序阅读 , 从零基础到开发简易 ButterKnife 注解框架的学习路径 ;
韩曙亮
2023/03/29
3030
100行代码拆解EventBus核心逻辑(三)
在前文的讲解中对 EventBus 的实现逻辑有了大概的理解之后,我们知道 Java 解析注解可以在运行时解析也可以在编译期间解析。由于运行时解析是通过反射来获取注解标记的类、方法、属性等对象,它的性能要受到反射的影响。因此在一些基础组件中更常见的做法是使用注解解析器技术,像 Dagger、 butterknife、 ARouter 以及本文所接触的 EventBus等框架库都是使用到了注解解析器的技术。接下来我们来实现一个注解解析器。(本文代码有点多)
阳仔
2019/12/17
5480
100行代码拆解EventBus核心逻辑(三)
Java--注解
@Retention还能指定两种,一种SOURCE级别,只在源码中,不维持至字节码:
aruba
2021/12/06
3280
Java--注解
Android 注解与注解处理器简述
  在Android开发中,注解是非常多的,如果不去了解,你可能感受不到注解的存在,一些框架用到的注解是很多的,例如Butterknife、Retrofit、Dagger2、Hilt、ViewBinding、DataBinding等等,下面简单的来了解一下注解。
晨曦_LLW
2022/10/24
7220
Android 注解与注解处理器简述
Java注解编译期处理AbstractProcessor详解
平时我们接触的框架大部分都是运行时注解,比如:@Autowire @Resoure @Bean 等等。
石臻臻的杂货铺[同名公众号]
2023/04/17
3K0
Java注解编译期处理AbstractProcessor详解
【Android APT】注解处理器 ( Element 注解节点相关操作 )
Android APT 学习进阶路径 : 推荐按照顺序阅读 , 从零基础到开发简易 ButterKnife 注解框架的学习路径 ;
韩曙亮
2023/03/29
2600
【Android APT】注解处理器 ( Element 注解节点相关操作 )
EventBus源码解析
时近年末,但是也没闲着。最近正好在看 EventBus 的源码。那就正好今天来说说 EventBus 的那些事儿。
俞其荣
2022/07/28
5370
EventBus源码解析
JVM系列六(自定义插入式注解器).
从前面 文章 中我们可以了解到,javac 的三个步骤中,程序员唯一能干预的就是注解处理器部分,注解处理器类似于编译器的插件,在这些插件里面,可以读取、修改、添加抽象语法树中的任意元素。因此,只要有足够的创意,程序员可以通过自定义插入式注解处理器来实现许多原本只能在编码中完成的事情。我们常见的 Lombok、Hibernate Validator 等都是基于自定义插入式注解器来实现的。
JMCui
2020/01/13
1.1K0
JVM系列六(自定义插入式注解器).
【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
在 【Android 组件化】路由组件 ( 路由组件结构 ) 博客中介绍了组件化中的 " 路由组件 " , 分为 " 自定义注解模块 " , " 注解处理器模块 " , " 依赖库模块 " 3 个模块 ;
韩曙亮
2023/03/29
4070
【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
Lombok天天用,却不知道它的原理是什么?
来源丨掘金 链接:https://juejin.im/post/5e54d38a6fb9a07cbf46b3ca
趣学程序-shaofeer
2020/06/29
3780
Lombok天天用,却不知道它的原理是什么?
apt 与 JavaPoet 自动生成代码
吴涛
2017/10/26
4.6K0
apt 与 JavaPoet 自动生成代码
什么是插入式注解,一文读懂!
插入式注解处理器在《深入理解Java虚拟机》一书中有一些介绍(前端编译篇有提到),但一直没有机会使用,直到碰到这个需求,觉得再合适不过了,就简单用了一下,这里做个记录。
终码一生
2023/08/22
4310
什么是插入式注解,一文读懂!
编译时注解(一)AbstractProcessor实战
Java中的注解(Annotation)是一个很神奇的东西,特别现在有很多Android库都是使用注解的方式来实现的。 我们并不讨论那些在运行时(Runtime)通过反射机制运行处理的注解,而是讨论在编译时(Compile time)处理的注解。下面便入手学习下Java注解处理器。
提莫队长
2019/02/21
7.5K1
Android注解快速入门和实用解析
文章较长,欢迎收藏后浅斟慢酌。主要介绍和分析了 RUNTIME 和 CLASS 下两种注解的使用,也欢迎讨论留言。
GSYTech
2018/08/22
4370
Android注解快速入门和实用解析
Lombok 原理分析与功能实现
这两天没什么重要的事情做,但是想着还要春招总觉得得学点什么才行,正巧想起来前几次面试的时候面试官总喜欢问一些框架的底层实现,但是我学东西比较倾向于用到啥学啥,因此在这些方面吃了很大的亏。而且其实很多框架也多而杂,代码起来费劲,无非就是几套设计模式套一套,用到的东西其实也就那么些,感觉没啥新意。刚这两天读”深入理解 JVM ”的时候突然想起来有个叫 Lombok 的东西以前一直不能理解他的实现原理,现在正好趁着闲暇的时间研究研究。
周三不加班
2019/06/04
1K0
聊聊如何运用JAVA注解处理器(APT)
APT(Annotation Processing Tool)它是Java编译期注解处理器,它可以让开发人员在编译期对注解进行处理,通过APT可以获取到注解和被注解对象的相关信息,并根据这些信息在编译期按我们的需求生成java代码模板或者配置文件(比如SPI文件或者spring.fatories)等。APT获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能
lyb-geek
2023/04/11
9320
聊聊如何运用JAVA注解处理器(APT)
自定义Android注解Part2:代码自动生成
上一期我们已经把butterknife-annotations中的注解变量都已经定义好了,分别为BindView、OnClick与Keep。
Rouse
2019/07/16
6240
自定义Android注解Part2:代码自动生成
手把手教你实现Android编译期注解
从早期令人惊艳的ButterKnife,到后来的以ARouter为首的各种路由框架,再到现在谷歌大力推行的Jetpack组件,越来越多的第三方框架都在使用编译期注解这门技术,可以说不管你是想要深入研究这些第三方框架的原理 还是要成为一个Android高级开发工程师,编译期注解都是你不得不好好掌握的一门基础技术。
2020labs小助手
2021/07/27
5130
Annotation Processor
要了解Annotation Processor,首先需要先了解什么是 Annotation。 Annotation : 是 Java 注解。 例如常见的 @Override @Nullable 等, 可以对类或者字段进行标记。 这些标记可以在反射时读取 或者 通过 Annotation Processor进行解析来自动生成一些对应的代码。
艳龙
2021/12/16
5560
相关推荐
Java中的注解处理器是什么,提供一个自定义注解处理器的实际案例
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档