Java注解

Posted by srtianxia on 2016-10-02

基本语法

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}

元注解

定义注解时,需要一些元注解
@Target定义你的注解应用于什么地方

  • 构造器的声明(CONSTRUCTOR)
  • 域声明(FIELD)
  • 局部变量声明(LOCAL_VAIABLE)
  • 方法声明(METHOD)
  • 包声明(PACKAGE)
  • 参数声明(PARAMETER)
  • 类,接口,或enum声明(TYPE)

@Retention定义该注解在哪一个级别可用

  • 源代码(SOURCE) 注解将被编译器丢弃
  • 类文件(CLASS) 注解在class文件中可用,但会被vm丢弃
  • 运行时(RUNTIME) vm在运行时也保留注解,可用通过反射读取注解信息

元素

在注解中,一般都会包含一些元素以表示某些值。当分析处理注解时,程序或者工具可以利用这些值,注解的元素看起来就像接口的方法,唯一的区别就是你可以为其指定默认值,没有元素的注解称为标记注解

注解可用的类型有

  • 基本数据类型
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
} ///:~

description元素有一个默认值,如果在注解某个方法时没有给出description的值,则该注解的处理器就会使用此元素的默认值

public class PasswordUtils {
@UseCase(id = 47, description =
"Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49, description =
"New passwords can't equal previously used ones")
public boolean checkForNewPassword(
List<String> prevPasswords, String password) {
return !prevPasswords.contains(password);
}
} ///:~

约束

编译器对元素的默认值有些过分挑剔。首先,元素不能有不确定的值,也就是说,元素必须要么具有默认值,要么使用注解时提供的元素的值。
其次,对于非基本类型的元素,无论是在原代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值。这个约束使得处理器很难表现为一个元素的存在或缺失的状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能自己定义一些特殊的值,例如空字符串或者负数,表示某个元素不存在。

注解处理器

如果没有用来读取注解的工具,那注解也不会比注释更有用。使用注解的过程中,很重要的一个部分是创建于使用注解处理器,JDK1.5之后便提供了反射机制,以帮助程序员构造这类工具。同时,他还提供了一个外部工具apt帮助程序员解析带有注解的java代码

反射

在学习类型信息的时候见过这些方法,就不赘述

public class UseCaseTracker {
public static void
trackUseCases(List<Integer> useCases, Class<?> cl) {
for(Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if(uc != null) {
System.out.println("Found Use Case:" + uc.id() +
" " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for(int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
} /* Output:
Found Use Case:47 Passwords must contain at least one numeric
Found Use Case:48 no description
Found Use Case:49 New passwords can't equal previously used ones
Warning: Missing use case-50
*///:~

apt处理注解

这里在thinking in java中使用的还是AnnotationProcessor作为处理器的基类,是com.sun.mirror.*包下的类,在这里使用最新的javax.annotation.processing.AbstractProcessor => AbstractProcessor,有必要提一下ButterKnifeEventBus也是采用了编译时注解的技术来减少反射带来的性能损耗,但是apt只能自动生成代码,运行的时候需要主动调用

//ButterKnife的主动调用过程
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Activity source) {
View sourceView = source.getWindow().getDecorView();
return createBinding(target, sourceView);
}
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
//通过类名创建
try {
Class<?> bindingClass = Class.forName(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
//view的绑定过程
public MainActivity_ViewBinding(T target, View source) {
this.target = target;
//注入过程
target.toolbar = Utils.findRequiredViewAsType(source, R.id.toolbar, "field 'toolbar'", Toolbar.class);
}

可以看到,大概的过程通过类名创建代理类的对象(也是反射,但是只是在构造的时候使用constructor.newInstance),在代理类对象的构造函数中完成了view的绑定过程