编译时注解(BufferKnife等)与运行时注解(otto),注解处理器APT

2023-11-23 08:58

本文主要是介绍编译时注解(BufferKnife等)与运行时注解(otto),注解处理器APT,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android路由开源库,阿里开源的ARouter路由。
 路由与注解。

MainDex 优化记- https://juejin.im/post/5c5bee986fb9a049bc4d1b58
此插件只作用于打包过程,编码过程无感知、无影响,删除注解类 -https://github.com/jokermonn/thinAnnotation

--  1.运行期注解(RunTime)利用反射去获取信息还是比较损耗性能的;
  2.编译期(Compile time)注解,以及处理编译期注解的手段APT和Javapoet,@Retention(RetentionPolicy.CLASS)。

  因为运行时注解涉及到反射,所以运行时注解的效率多少会受到影响,现在很多的开源项目使用的是编译时注解。编译时注解(如@Override@Deprecated)和运行时注解(如@Autowired)都是通过反射API可以得到,所以他们到底有什么区别呢?
  个人理解:编译时注解并不是通过反射API来获得注解类的,都没有运行呢何来的反射?我们在编译程序时可以通过“-processor”选项在编译时指定一个Annotation处理器,该处理器实现Processor接口,通过该接口的方法来检查获取类中的注解类,你可以看一下Processor的process方法,对注解的处理就可以在这个方法中实现,而注解类就可以从该方法传入的属性中获取。举一个编译时注解的用处,就是在编译时通过注解生成xml文件,好像hibernate就是这么做的 。
  java提供了一个javax.lang.model包,来描述编译时的信息。与之相对的是我们更为熟悉的java.lang.reflect包,用以描述运行时的信息。两个包在某种程度上有很大的相似性。 这个javax.lang.model包里,有两个重要的类TypeMirror和Element。它们共同保存了一个代码片段的信息,可以通过asElement()和asType()相互转化。

APT注解学习小案例,比较系统性学习注解并且应用实践- https://github.com/yangchong211/YCApt

 Java元注解,元注解是指注解的注解,包括@Retention @Target @Document @Inherited四种。

-- 元注解@Retention 按生命周期来划分可分为3类:(注解的“存活时间”),即注解保留策略
1、RetentionPolicy.SOURCE:注解只保留在Java源文件中,当Java文件编译成class文件的时候,注解被遗弃擦除,类似于@Override只是给程序员看的;
2、RetentionPolicy.CLASS:注解被保留到class文件中,但jvm加载class文件时候被遗弃,这是默认的生命周期,注解在编译的时候会存在,在运行的时候就会擦除,(编译时注解);
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,用反射来实现,(运行时注解);
   这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。明确生命周期长度 SOURCE < CLASS < RUNTIME

> 编译时注解,apt+javaPoet ,(EventBus3.0,ButterKnife)
  其中apt+javaPoet目前也是应用比较广泛,在一些大的开源库,如EventBus3.0+,页面路由 ARout、Dagger、Retrofit等均有使用的身影,注解不仅仅是通过反射一种方式来使用,也可以使用APT在编译期处理:
 android-apt注解ioc-apt-sample- https://github.com/hymanAndroid/ioc-apt-sample
 通过注解来生成java源文件javapoet- https://github.com/square/javapoet

-- Annotation processing 是javac中用于编译时扫描和解析Java注解的工具.
 ButterKnife APT涉及3个核心技术:BufferKnife千万不要说成用注解+反射哦~,
  1.编译期注解;2.APT(注解处理器);3.javaPoet(自动生成代码)

Android 如何编写基于编译时注解的项目- https://blog.csdn.net/lmj623565791/article/details/51931859
Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架- https://www.cnblogs.com/whoislcj/p/6168641.html
编译期注解之APT- https://blog.csdn.net/xsf50717/article/details/54318874
  
  处理编译器注解的第一个手段就是APT(Annotation Processor Tool),即注解处理器。APT是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解,一个注解的注解处理器,以java代码(或者编译过的字节码)作为输入,生成.java文件作为输出,核心是交给自己定义的处理器去处理。
  注解处理器,即 annotation processor tool,简称apt,apt是在使用ButterKnife的时候。我一看,嘿,编译时注解、自动生成样板代码findViewById()...,这么厉害,研究一下。这的确和运行时通过反射处理注解,是完全不同的路子,能达到一些意想不到的效果。
   Annotation注解在Android的开发中的使用越来越普遍,例如EventBus、ButterKnife、Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直到后来android-apt的出现通过注解根据反射机制动态编译生成代码的方式来解决在运行时不再使用发射机制,不过随着android-apt的退出不再维护,我们今天利用Android studio的官方插件annotationProcessor来实现一下自己的ButterKnife UI注解框架。
  java官方可能是出于注解性能考虑,很快在java_1.6推出了”注解处理器”这个东西。注解处理器,即 annotation processor tool,简称apt。它使得我们能在“编译时”处理注解,既保留了注解的功能,又不会在“运行时”造成性能损耗。

  任何的注解处理器的编写方式基本都遵循着收集信息、生成代理类的步骤。使用编译时注解的成员变量一般都不允许private修饰符修饰(有的允许,但是需要提供getter,setter访问方法)。

-- BufferKnife使用的注解是编译时的吗? 是的。
从 ButterKnife 到“编译时注解”实战- https://blog.csdn.net/a153614131/article/details/53248571
一开始,注解是为编译时检查服务的,不会影响程序运行,反而增强了程序的可读性。常见的有:
 @Override: 检查是否正确重写
 @Deprecated: 表示该函数已弃用,会划一条横线
 @NonNull: 检查参数非空。空指针可以在编译时发现,而不必等到运行时抛出NullPointerException

> 运行时注解;otto运行时注解。运行时注解靠反射获取注解。
自定义注解之运行时注解(RetentionPolicy.RUNTIME)- https://blog.csdn.net/github_35180164/article/details/52118286
 
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Documented//说明该注解将被包含在javadoc中  。

> 不同的注解处理器
custom annotation processor- https://github.com/yuweiguocn/CustomAnnotation
注解框架—AndroidAnnotations - https://github.com/androidannotations/androidannotations

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        Map<String, AnnotatedClass> classMap = new HashMap<>();

        // 得到所有注解@Interface的Element集合
        Set<? extends Element> elementSet = env.getElementsAnnotatedWith(Interface.class);

        for (Element e : elementSet) {
            if (e.getKind() != ElementKind.METHOD) {
                error(e, "错误的注解类型,只有函数能够被该 @%s 注解处理", Interface.class.getSimpleName());
                return true;
            }

            ExecutableElement element = (ExecutableElement) e;
            AnnotatedMethod annotatedMethod = new AnnotatedMethod(element);

            String classname = annotatedMethod.getSimpleClassName();
            AnnotatedClass annotatedClass = classMap.get(classname);
            if (annotatedClass == null) {
                PackageElement pkg = elementUtils.getPackageOf(element);
                annotatedClass = new AnnotatedClass(pkg.getQualifiedName().toString(), element.getAnnotation(Interface.class).value());
                annotatedClass.addMethod(annotatedMethod);
                classMap.put(classname, annotatedClass);
            } else
                annotatedClass.addMethod(annotatedMethod);

        }
        // 代码生成
        for (AnnotatedClass annotatedClass : classMap.values()) {
            annotatedClass.generateCode(elementUtils, filer);
        }
        return false;
    }

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();
    }
}

-- 每个自定义的处理器都要继承虚处理器AbstractProcessor,实现其关键的几个方法:
public class MyProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment env){ }
    @Override
    public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }

    @Override
    public Set<String> getSupportedAnnotationTypes() { }

    @Override
    public SourceVersion getSupportedSourceVersion() { }
}

这篇关于编译时注解(BufferKnife等)与运行时注解(otto),注解处理器APT的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/416784

相关文章

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用

如何在运行时修改serialVersionUID

优质博文:IT-BLOG-CN 问题 我正在使用第三方库连接到外部系统,一切运行正常,但突然出现序列化错误 java.io.InvalidClassException: com.essbase.api.base.EssException; local class incompatible: stream classdesc serialVersionUID = 90314637791991

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

Golang test编译使用

创建文件my_test.go package testsimport "testing"func TestMy(t *testing.T) {t.Log("TestMy")} 通常用法: $ go test -v -run TestMy my_test.go=== RUN TestMyTestMy: my_test.go:6: TestMy--- PASS: TestMy (0.

win7+ii7+tomcat7运行javaWeb开发的程序

转载请注明出处:陈科肇 1.前提准备: 操作系统:windows 7 旗舰版   x64 JDK:jdk1.7.0_79_x64(安装目录:D:\JAVA\jdk1.7.0_79_x64) tomcat:32-bit64-bit Windows Service Installer(安装目录:D:\0tomcat7SerV) tomcat-connectors:tomcat-connect

php 7之PhpStorm + Nginx + Xdebug运行调试

操作环境: windows PHP 7.1.10 PhpStorm-2017.2.4 Xdebug 2.5.4 Xdebug helper 1.6.1 nginx-1.12.2 注意查看端口占用情况 netstat -ano //查看所以端口netstat -aon|findstr "80" //查看指定端口占用情况 比如80端口查询情况 TCP 0.0.0.0:8

C++/《C/C++程序编译流程》

程序的基本流程如图:   1.预处理        预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理将所有的“#define”删除,并且展开所有的宏定义处理所有的条件编译指令,如:“#if”、“

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte