注解的自我理解

目录

  1. 什么是注解?
  2. 注解使用场景?
  3. 注解如何使用?
  4. 主流框架的注解分析
  5. 参考文章

 

1. 什么是注解?

    注解是在JDK1.5引入的,官方文档中的解释为元数据,就是一个解释数据的数据。在源代码中对代码进行解释的数据。很多人把他来理解为标签,见名知其意思。下面通过一个常见的代码注解:

@Override
public String toString() {
    // TODO Auto-generated method stub
    return super.toString();
}

   在這段代碼中,@Override就是一個注解 ,熟悉Java的人都知道,Override是复写的意思,即使删除@Override的注解,这段代码一样可以正常运行。那么问题来了,这个注解有什么作用?在这段代码中toString被打上了@Override的注解,意味着这个类的父类也有这个方法,如果你强行将toString方法改成父类没有的方法,编译器就能提示你错误信息(The method xx of type xx must override or implement a supertype method)。那么编译器是如何判断这个两个方法是不一样的呢,在Java的源码中@Override的是这样的:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

** Java提供的标准注解有:@Override[重写]、@Deprecated[不鼓励使用]、@SuppressWarnings[忽略警告]等

从这个简单的注解源码中,我们看到的只有@Target和@Retention这两个配置,仔细查阅文档Java的注解声明四种元注解有如下这些:

@Document

 Indicates that annotations with a type are to be documented by javadoc and similar tools   by default.  This type should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients.  If a type declaration is annotated with Documented, its annotations become part of the public API of theannotated elements.

 声明是否将注释信息打入到java文档中,添加这个注解注释的信息将会成为公共的API为开 发人员使用

@Target

Indicates the contexts in which an annotation type is applicable. The declaration contexts and type contexts in which an annotation type may be applicable are specified in JLS 9.6.4.1, and denoted in source code by enum constants of {ElementTypava.lang.annotation.ElementType}.

声明这个注解的可用范围,用ElementType的枚举来判定:

 

 ElementType.TYPE                           ---- 用于描述类、接口(包括注解类型)或enum声明
 ElementType.FIELD                          ---- 用于成员变量声明(包括枚举的不变量)
 ElementType. METHOD                    --- 用于方法声明
 ElementType.PARAMETER              --- 参数声明 
 ElementType.CONSTRUCTOR,       --- 构造器声明
 ElementType.LOCAL_VARIABLE,    ---- 变量声明
 ElementType.ANNOTATION_TYPE, ---- 注解声明

 ElementType.PACKAGE,                  ----- 包声明

  -----------------------------------------since 1.8-------------------------------------
 ElementType.TYPE_PARAMETER
 ElementType.TYPE_USE

 

@Retention

Indicates how long annotations with the annotated type are to be retained.  If no Retention annotation is present on  an annotation type declaration, the retention policy defaults to {RetentionPolicy.CLASS}

声明注解的保存的周期,不声明默认使用RetentionPolicy.CLASS


RetentionPolicy.SOURCE ----  在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义, 所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

 

RetentionPolicy.CLASS,    ----- 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。 (编译时Annotation,对于这类注解,我们通常借助apt工具注解处理器Processor进行解析。) 

RetentionPolicy.RUNTIME  ----- 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。(运行时Annotation

@Inherited

Indicates that an annotation type is automatically inherited.

允许子类继承父类中的注解

从上面对表格中,和@Override的源代码中,我们知道@Override的作用范围是在方法中,存活的是在编译阶段。每次你在使用@Override时候,编译器会通过注解去接触父类是否有这个方法,就可以在编译期间发现这个方法名不一致的错误。

2.注解的使用场景

      在Java技术文档中提供了以下几种解释:

  • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress * warnings.
  • Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
  • Runtime processing — Some annotations are available to be examined at runtime.

  • 为编译器提供辅助信息 — Annotations可以为编译器提供而外信息,以便于检测错误,抑制警告等 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。.(文档注释,检查校验代码)
  • 编译源代码时进行而外操作 — 软件工具可以通过处理Annotation信息来生成原代码,xml文件等等.(Dagger2,ButterKnife )
  • 运行时处理 —   有一些annotation甚至可以在程序运行时被检测,使用.(Xutils,EventBus

3.注解如何使用

    编写自定义注解如下:

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Test {
	int max() default 0;

	int min() default 0;

	String description() default "";

}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sex {
	enum GENDER {
		MAN, WOMAN
	}

	GENDER value() default GENDER.MAN;
}

自定义注解(接口)是一个继承了Annotation接口的特殊接口,这个结论来源于Java注解(Annotation)原理详解,如果不是很理解的可以移步到文章出处查看过程。

 3.1  注解定义的支持的类型

  • 所有基本数据类型(int,float,double,boolean,byte,char,long,short)
  • String 类型
  • Class类型
  • enum类型
  • Annotation类型(xutils中的OnClick的注解中使用了这种例子)
  • 以上所有类型的数组

3.2  注解应满足的规则

  • 修饰符只能是public 或默认(default)
  • 参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和String,Enum,Class,annotations及这些类型的数组
  • 如果只有一个参数成员,最好将名称设为”value”
  • 注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常使用空字符串或0作默认值
  • 在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值

3.3 注解的使用方法

    方法定义在作用范围是成员变量,只要在成员变量上方添加注解即可,如下代码:

public class AnnotationTest {
	
	@Test(min=6 , max = 10, description= "用户名长度在6-10个字符之间" )
	private String name;
	@Test(min=6 , max = 10, description= "用户名长度在6-10个字符之间" )
	private String password;
	@Sex(value = GENDER.MAN)
	private String sex;
	
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
}

添加注解之后并不能将遇见的逻辑加入代码中,因为注解只是一个特殊的接口,要想注解中的逻辑执行在代码中,必须使用注解处理器,如下代码:

/** 注解处理器 */
public static void valid(Object obj) throws IllegalAccessException {
		Class<?> clazz = obj.getClass();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.isAnnotationPresent(Test.class)) {
				Test test = field.getAnnotation(Test.class);// 获取属性上的@Test注解
				if (test != null) {
					field.setAccessible(true);// 设置属性可访问

					if ("class java.lang.String".equals(field.getGenericType().toString())) {// 字符串类型的才判断长度
						String value = (String) field.get(obj);
						if (value != null && ((value.length() > test.max()) || value.length() < test.min())) {
							System.out.println(test.description());
						}
					}
				}
			} else if (field.isAnnotationPresent(Sex.class)) {

				Sex sex = field.getAnnotation(Sex.class);
				if (sex != null) {
					field.setAccessible(true);// 设置属性可访问
					if (sex.value() == GENDER.MAN) {
						System.out.println(sex.value() + "==这是个男孩");
					} else {
						System.out.println(sex.value() + "==这是个女孩");
					}
					System.out.println(sex.description());
				}
			}
		}

上述代碼是將一個類中注解的内容處理成代碼邏輯測試代碼如下:

public static void main(String[] args) {
    AnnotationTest annotation = new AnnotationTest();
    annotation.setName("abcfg");
    annotation.setPassword("1234567sdfdafdgfdgfgfdgfd890");
    try {
            valid(annotation);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
	    }
}
		
	

控制台輸出:

至此就完成了一個簡單的自定義注解的使用。:

NOTE:在执行代码的过程中我将Sex的注解中的@Retention的保留周期变成了RetentionPolicy.CLASS,在执行field.isAnnotationPresent(Sex.class)的时候就会找不到相应的代码而出现如下的结果:

注解运行结果2

4.主流注解框架的分析

4.1  Xutils注解框架源码分析

 查看的是Xutils的2.4.1的源码,源码中有的包专门是用来处理注解的部分的定义的,下图是Xutils中所有的自定义注解:

这里限于篇幅的原因,只针对常用的ViewUtils作分析:

ViewUtils使用:

1.定义注解。ViewInject的注解代码是这样定义的:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {

    int value();

    /* parent view id */
    int parentId() default 0;
}

2.动态利用反射调用注解代码。ViewUtils中的重要代码:


    private static void injectObject(Object handler, ViewFinder finder) {

        Class<?> handlerType = handler.getClass();

        // inject ContentView
        ContentView contentView = handlerType.getAnnotation(ContentView.class);
        if (contentView != null) {
            try {
                Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
                setContentViewMethod.invoke(handler, contentView.value());
            } catch (Throwable e) {
                LogUtils.e(e.getMessage(), e);
            }
        }

        // inject view
        Field[] fields = handlerType.getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);// 找到注解并实例化
                if (viewInject != null) {
                    try {
                        View view = finder.findViewById(viewInject.value(), viewInject.parentId());//ViewFinder包装了Activity等多个View
                        if (view != null) {
                            field.setAccessible(true);
                            field.set(handler, view);//对View进行赋值
                        }
                    } catch (Throwable e) {
                        LogUtils.e(e.getMessage(), e);
                    }
                } else {
                    ResInject resInject = field.getAnnotation(ResInject.class);
                    if (resInject != null) {
                        try {
                            Object res = ResLoader.loadRes(
                                    resInject.type(), finder.getContext(), resInject.id());
                            if (res != null) {
                                field.setAccessible(true);
                                field.set(handler, res);
                            }
                        } catch (Throwable e) {
                            LogUtils.e(e.getMessage(), e);
                        }
                    } else {
                        PreferenceInject preferenceInject = field.getAnnotation(PreferenceInject.class);
                        if (preferenceInject != null) {
                            try {
                                Preference preference = finder.findPreference(preferenceInject.value());
                                if (preference != null) {
                                    field.setAccessible(true);
                                    field.set(handler, preference);
                                }
                            } catch (Throwable e) {
                                LogUtils.e(e.getMessage(), e);
                            }
                        }
                    }
                }
            }
        }

        // inject event
        Method[] methods = handlerType.getDeclaredMethods();
        if (methods != null && methods.length > 0) {
            for (Method method : methods) {
                Annotation[] annotations = method.getDeclaredAnnotations();
                if (annotations != null && annotations.length > 0) {
                    for (Annotation annotation : annotations) {
                        Class<?> annType = annotation.annotationType();
                        if (annType.getAnnotation(EventBase.class) != null) {
                            method.setAccessible(true);
                            try {
                                // ProGuard:-keep class * extends java.lang.annotation.Annotation { *; }
                                Method valueMethod = annType.getDeclaredMethod("value");
                                Method parentIdMethod = null;
                                try {
                                    parentIdMethod = annType.getDeclaredMethod("parentId");
                                } catch (Throwable e) {
                                }
                                Object values = valueMethod.invoke(annotation);
                                Object parentIds = parentIdMethod == null ? null : parentIdMethod.invoke(annotation);
                                int parentIdsLen = parentIds == null ? 0 : Array.getLength(parentIds);
                                int len = Array.getLength(values);
                                for (int i = 0; i < len; i++) {
                                    ViewInjectInfo info = new ViewInjectInfo();
                                    info.value = Array.get(values, i);
                                    info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
                                    EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
                                }
                            } catch (Throwable e) {
                                LogUtils.e(e.getMessage(), e);
                            }
                        }
                    }
                }
            }
        }
    }

从上述代码中我们很容易找到ViewInject注解部分,在ViewUtils的代码中injectObject方法中,可以定位到

而另外关于OnClick这一类的事件监听,由于涉及到注解中的一个新的Type(ElementType.ANNOTATION_TYPE),在上述基本的例子没有讲解到,在这里针对OnClick的监听注解做一个分析。所有的Event注解中有一个基础的注解EventBase,如下:

@Target(ElementType.ANNOTATION_TYPE)//在注解中使用的类型
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
    Class<?> listenerType();

    String listenerSetter();

    String methodName();
}

onClick的注解中也通过注解的方式将使用了EventBase这个注解,如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(
        listenerType = View.OnClickListener.class,
        listenerSetter = "setOnClickListener",
        methodName = "onClick")
public @interface OnClick {
    int[] value();

    int[] parentId() default 0;
}

在上述ViewUtils的代码中在动态注入的代码中,

在EvenListenerManager中找到了利用反射調用OnClickListener的代码:

至此,我们就简单的完成了XUtils中ViewUtils中的注解调用。

4.2  EventBus注解框架源码分析

      1.定义注解代码。在使用EventBus3.以后我们是使用一个@Subscribe的注解,注解代码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    boolean sticky() default false;

    int priority() default 0;
}
public enum ThreadMode {
    POSTING,
    MAIN,
    MAIN_ORDERED,
    BACKGROUND,
    ASYNC;

    private ThreadMode() {
    }
}

注解中的定义的参数成员有三个,分别为线程模式threadMode,sticky和priority,在使用Eventbus的时候我们很简单一句代码就搞定,可是这个注解是如何写入到代码逻辑中的呢?talk is cheap,show you the source code.

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriberClass);//定位到通过字节码找方法
        synchronized(this) {
            Iterator var5 = subscriberMethods.iterator();

            while(var5.hasNext()) {
                SubscriberMethod subscriberMethod = (SubscriberMethod)var5.next();
                this.subscribe(subscriber, subscriberMethod);
            }

        }
    }

在SubscriberMethodFinder中findSubcriberMethods的代码中我们看到了:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = (List)METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        } else {
            if (this.ignoreGeneratedIndex) {
                subscriberMethods = this.findUsingReflection(subscriberClass);//追踪下去
            } else {
                subscriberMethods = this.findUsingInfo(subscriberClass);
            }

            if (subscriberMethods.isEmpty()) {
                throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation");
            } else {
                METHOD_CACHE.put(subscriberClass, subscriberMethods);
                return subscriberMethods;
            }
        }
    }

一路跟踪之后我们找到了使用注解处理器如下:

private void findUsingReflectionInSingleClass(SubscriberMethodFinder.FindState findState) {
        Method[] methods;
        try {
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable var12) {
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }

        Method[] var3 = methods;
        int var4 = methods.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Method method = var3[var5];
            int modifiers = method.getModifiers();
            if ((modifiers & 1) != 0 && (modifiers & 5192) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = (Subscribe)method.getAnnotation(Subscribe.class);//获取注解实体,就是这里了!!!
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (this.strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (this.strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }

    }

从上面的代码中可以看出EventBus的注解部分是通过反射的方式写入到代码逻辑的中的。

4.3 ButterKnife注解框架解析

    在上述的第二小节中,我们提到了注解的三个比较重要的使用场景,第一个比较简单明了,第三个在上述的例子和框架中都有使用到,唯独第二个使用场景没有理解到,下面就从ButterKnife这个注解框架来理解注解的第二种使用场景:编译源代码时进行而外操作。

   要理解ButterKnife框架首先要理解编译时Annotation的标准流程:

  • 定义注解:@interface
  • 定义注解处理器: 重写AbstractProcessor的2个方法
  • 实现注解处理器: 可以使用JavaPoet生成代码文件,详细文档介绍移步 javapoet
  • 注册注解处理器:@AutoService
  • 使用注解,build工程

4.3.1  定义注解,ButterKnife定义的注解,如下:

       

         

4.3.2 定义注解处理器

   注解处理器是一个继承自AbstractProcessor的一个Java类,内部主要定义了:

  • 要处理的注解是谁:通过getSupportedAnnotationTypes方法进行指定
  • 如何处理这个注解:通过process方法进行指定
public abstract class AbstractProcessor implements Processor {
   ...

    /**
     *  返回注解的类名
     */
    public Set<String> getSupportedAnnotationTypes() {
            SupportedAnnotationTypes sat =this.getClass().getAnnotation(SupportedAnnotationTypes.class);
            if  (sat == null) {
                if (isInitialized())
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                             "No SupportedAnnotationTypes annotation " +
                                                             "found on " + this.getClass().getName() +
                                                             ", returning an empty set.");
                return Collections.emptySet();
            }
            else
                return arrayToSet(sat.value());
        }

   
  ...
     
    /**
     *   如何处理注解
     */
    public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
}

   ButterKnife 的注解处理器的主要部分如下:

public class ButterKnifeProcessor extends AbstractProcessor {
	
	 /**
      *返回的Set集合,是注解类ClassName的集合
      *1.这个方法主要用来指定,当前的注解处理器,需要处理哪些Annotation
      *2.这些Annotation,将会通过一个Set集合的形式返回
      *3.Set集合内部存储的是,所有注解的类名className
      */
	@Override
	public Set<String> getSupportedAnnotationTypes() {
		Set<String> types = new LinkedHashSet<>();
	    for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
	      types.add(annotation.getCanonicalName());
	    }
//		return super.getSupportedAnnotationTypes();
		return types;
	}
	
	private Set<Class<? extends Annotation>> getSupportedAnnotations() {
	    Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();

	    annotations.add(BindAnim.class);
	    annotations.add(BindArray.class);
	    annotations.add(BindBitmap.class);
	    annotations.add(BindBool.class);
	    annotations.add(BindColor.class);
	    annotations.add(BindDimen.class);
	    annotations.add(BindDrawable.class);
	    annotations.add(BindFloat.class);
	    annotations.add(BindFont.class);
	    annotations.add(BindInt.class);
	    annotations.add(BindString.class);
	    annotations.add(BindView.class);
	    annotations.add(BindViews.class);
	    annotations.addAll(LISTENERS);

	    return annotations;
	  }
	/**
	 * 
	 *   如何处理注解
     *   这个方法主要用来定义,针对这些注解,我们要做哪些相应的处理
     *  通常,我们会在方法内部,创建相关的Java类、或是XML文件
     *返回值表示,当前Processor是否接收这组Annotation,如果接收,后续其他的Processor将不会继续处理
     *   
	 */
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		 Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);

	        for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
	            TypeElement typeElement = entry.getKey();
	            BindingSet binding = entry.getValue();
	            JavaFile javaFile = binding.brewJava(sdk, debuggable);
	            try {
                    //创建文件
	                javaFile.writeTo(filer);
	            } catch (IOException e) {
	                error(typeElement, "Unable to write binding for type %s: %s",
	                        typeElement, e.getMessage());
	            }
	        }

		return false;
	}

}

 

通过简单的两套逻辑大体上看到了butterKnife的实现流程,对于实现注解处理器过程和注册处理的过程,ButterKnife使用了两个比较好的库:javapoet 和autoservice

4.3.3  实现注解处理器

    

findAndParseTargets这个方法主要基于是将RoundEvironment这个里面的注解标签使用Map大型是进行了封装,由于代码较多,只截取了一部分代码:

   private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
        Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
        Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();

         ... 省略代码...
        // Process each @BindView element.
        for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
            // we don't SuperficialValidation.validateElement(element)
            // so that an unresolved View type can be generated by later processing rounds
            try {
                parseBindView(element, builderMap, erasedTargetNames);
            } catch (Exception e) {
                logParsingError(element, BindView.class, e);
            }
        }

        ..省略代码..
        // Associate superclass binders with their subclass binders. This is a queue-based tree walk
        // which starts at the roots (superclasses) and walks to the leafs (subclasses).
        Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
                new ArrayDeque<>(builderMap.entrySet());
        Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
        while (!entries.isEmpty()) {
            Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();

            TypeElement type = entry.getKey();
            BindingSet.Builder builder = entry.getValue();

            TypeElement parentType = findParentType(type, erasedTargetNames);
            if (parentType == null) {
                bindingMap.put(type, builder.build());
            } else {
                BindingSet parentBinding = bindingMap.get(parentType);
                if (parentBinding != null) {
                    builder.setParent(parentBinding);
                    bindingMap.put(type, builder.build());
                } else {
                    // Has a superclass binding but we haven't built it yet. Re-enqueue for later.
                    entries.addLast(entry);
                }
            }
        }

        return bindingMap;
    }

  BindingSet的brewJava方法内部是这样实现的:

JavaFile brewJava(int sdk, boolean debuggable) {
    TypeSpec bindingConfiguration = createType(sdk, debuggable);
    return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
        .addFileComment("Generated code from Butter Knife. Do not modify!")
        .build();
  }

 将字节码信息拼装,使用了javapoet库 ,最后调用JavaFile的writeTo方法生成创建文件。

4.3.4 注册注解器服务

        这个过程需要引入autoservice这个依赖库,(注册注解处理器,就是告诉APT这个类是一个Annotation Processor) 并在相应的AbstractProcessor中类上加上@AutoService(Processor.class),如下:

       添加依赖:

   compileOnly 'com.google.auto.service:auto-service:1.0-rc4'

      添加注解,完成快熟注册

      

4.3.5 build 工程    

   完成以上步骤,我们build下工程,就会在build目录下自动生成对应的java/class文件

  • java文件在build/generated文件夹下
  • class文件在build/intermediates文件夹下

本文只是对ButterKnife的实现过程进行初步介绍,如要定义自己的注解器,移步到 自定义轻量级ButterKnife  和 Android APT 了解更多。第一次写博客,不足之处,敬请指正。

5.参考文章

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值