ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

butterknife依赖注入框架源码解析

2021-03-13 22:01:10  阅读:286  来源: 互联网

标签:ElementType java Target public butterknife 源码 注解 解析 class


butterknife原理:编译时注解。

java注解(四种元注解:@Retention @Target @Document @Inherited)

  • @Document:说明该注解将被包含在javadoc中
  • @Retention:注解的保留位置         
    @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
    @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
    @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
  • @Target:注解的作用目标
    @Target(ElementType.TYPE) //接口、类、枚举、注解
    @Target(ElementType.FIELD) //字段、枚举的常量
    @Target(ElementType.METHOD) //方法
    @Target(ElementType.PARAMETER) //方法参数
    @Target(ElementType.CONSTRUCTOR) //构造函数
    @Target(ElementType.LOCAL_VARIABLE)//局部变量
    @Target(ElementType.ANNOTATION_TYPE)//注解
    @Target(ElementType.PACKAGE) ///包
  • @Inherited:说明子类可以继承父类中的该注解

APT(注解处理器)

定义:
APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。 简单来说就是在编译期,通过注解生成.java文件。

  • 注解处理器(Annotation Processor)是Javac的一个工具。
  • 每一个处理器都是继承于AbstractProcessor
  • init方法中的入参ProcessingEnvironment 的变量Filer ,Elements ,Types

AbstractProcessor
APT大概就是你声明的注解的生命周期为CLASS,然后继承AbstractProcessor类。继承这个类后,在编译的时候,编译器会扫描所有带有你要处理的注解的类,然后再调用AbstractProcessor的process方法,对注解进行处理,那么我们就可以在处理的时候,动态生成绑定事件或者控件的java代码,然后在运行的时候,直接调用bind方法完成绑定。

AbstractProcessor的四个核心方法:

init;process;getSupportedAnnotationTypes;getSupportedSourceVersion
其中process最核心

AbstractProcessor的init方法如下:

public synchronized void init(ProcessingEnvironment var1) {

ProcessingEnvironment 里面的重要变量

public interface ProcessingEnvironment {
    Map<String, String> getOptions();

    Messager getMessager();
//创建文件用的
    Filer getFiler();
//源代码
    Elements getElementUtils();
//源代码类型信息
    Types getTypeUtils();

    SourceVersion getSourceVersion();

    Locale getLocale();
}

AbstractProcessor里面最核心的是process方法

拓展:
getSupportedAnnotationTypes

//返回支持的注解的类型
    public Set<String> getSupportedAnnotationTypes() {
        SupportedAnnotationTypes var1 = (SupportedAnnotationTypes)this.getClass().getAnnotation(SupportedAnnotationTypes.class);
        if (var1 == null) {
            if (this.isInitialized()) {
                this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedAnnotationTypes annotation found on " + this.getClass().getName() + ", returning an empty set.");
            }

            return Collections.emptySet();
        } else {
            return arrayToSet(var1.value());
        }
    }

//指定你所适用的Java版本
    public SourceVersion getSupportedSourceVersion() {
        SupportedSourceVersion var1 = (SupportedSourceVersion)this.getClass().getAnnotation(SupportedSourceVersion.class);
        SourceVersion var2 = null;
        if (var1 == null) {
            var2 = SourceVersion.RELEASE_6;
            if (this.isInitialized()) {
                this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedSourceVersion annotation found on " + this.getClass().getName() + ", returning " + var2 + ".");
            }
        } else {
            var2 = var1.value();
        }

        return var2;
    }

APT的流程:
1、声明的注解的生命周期为CLASS
2、继承AbstractProcessor类(编辑器会扫描要处理的注解)
3、调用process处理注解

拓展下反射

反射+运行时注解举例
运行时获取和使用类信息

反射的作用
1、判断任意一个对象所属的类
2、构造任意一个类的对象
3、判断任意一个类所具有的成员变量和方法(反射甚至可以调用private方法)
4、调用任意一个对象的方法

反射的简单使用:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestInterface {
    int value() default 20;

}

//客户端调用
public class ReflecMain {
    @TestInterface(88)
    public int age;

    public void handle() {
        try {
            Class clazz = ReflecMain.class;
            Field field = clazz.getField("age");
            TestInterface testInterface = field.getAnnotation(TestInterface.class);
            int value = testInterface.value();
            //可以得到value的值的88
            Log.v("tag", "value==" + value);
          

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

butterknife的工作原理:

1、编译的时候扫描注解,并做相应的处理,生成java代码,生成java代码是调用javapoet库生成的,同时通过@click注解编译的时候动态生成java文件,就是利用apt,编译器又会把java文件编译成class文件。
2、调用ButterKnife.bind(this)方法的时候,会将ID和上下文绑定在一起

总结:
通过process方法处理注解,调用squre公司开源的javapoet库的brewJava方法来生成代码,然后通过brewJava中createType方法生成一些类型和添加findviewbyid。

最终总结:

直接看AbstractProcessor的实现类:ButterKnifeProcessor的process方法,process方法拿到注解后放在一个HashMap中,遍历集合,通过squre公司开源的javapoet库生成java代码。

butterknife的原理:简单来说就是通过APT生成java代码(就是把注解的代码生成正常的findviewbyid的代码就可以了)。

标签:ElementType,java,Target,public,butterknife,源码,注解,解析,class
来源: https://blog.csdn.net/zx_android/article/details/114764724

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有