编译时注解(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

相关文章

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

MySQL多实例管理如何在一台主机上运行多个mysql

《MySQL多实例管理如何在一台主机上运行多个mysql》文章详解了在Linux主机上通过二进制方式安装MySQL多实例的步骤,涵盖端口配置、数据目录准备、初始化与启动流程,以及排错方法,适用于构建读... 目录一、什么是mysql多实例二、二进制方式安装MySQL1.获取二进制代码包2.安装基础依赖3.清

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

Java中使用 @Builder 注解的简单示例

《Java中使用@Builder注解的简单示例》@Builder简化构建但存在复杂性,需配合其他注解,导致可变性、抽象类型处理难题,链式编程非最佳实践,适合长期对象,避免与@Data混用,改用@G... 目录一、案例二、不足之处大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

spring中的@MapperScan注解属性解析

《spring中的@MapperScan注解属性解析》@MapperScan是Spring集成MyBatis时自动扫描Mapper接口的注解,简化配置并支持多数据源,通过属性控制扫描路径和过滤条件,利... 目录一、核心功能与作用二、注解属性解析三、底层实现原理四、使用场景与最佳实践五、注意事项与常见问题六

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)

《k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)》本文记录在K8s上运行的MySQL/MariaDB备份方案,通过工具容器执行mysqldump,结合定时任务实... 目录前言一、获取需要备份的数据库的信息二、备份步骤1.准备工作(X86)1.准备工作(arm)2.手