一、概念
Annontation是Java5开始引入的新特征。中文名称一般叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。二、原理
Annotation其实是一种接口。通过Java的反射机制相关的API来访问annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。利用反射处理注解的方式是运行时注解,还有一类是编译时注解。它们可以定义常量、静态成员类型(比如枚举类型定义)。Annotation类型也可以如接口一般被实现或者继承
三、作用
a. 标记,用于告诉编译器一些信息,例如@SuppressWarnings、@Override
b. 编译时动态处理,如动态生成代码,例如ButterKnife,一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的~~会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别。
c. 运行时动态处理,如得到注解信息,例如xutils , afinal
四、元注解
/** * 作用于类、接口(包括注释类型)或枚举声明 */ @Target(ElementType.TYPE) public @interface Table { /** * 数据表名称注解,默认值为类名称 * @return */ public String tableName() default "className"; } /** * 作用域成员变量 */ @Target(ElementType.FIELD) public @interface NoDBColumn { }
@Retention:
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。 作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)取值(RetentionPoicy)有:1.SOURCE:在源文件中有效(即源文件保留)2.CLASS:在class文件中有效(即class保留)3.RUNTIME:在运行时有效(即运行时保留)Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。package com.cn.annotation.test1; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; //作用于成员变量 @Target(ElementType.FIELD) // 在运行时有效 @Retention(RetentionPolicy.RUNTIME) public @interface Column { // 注解变量,默认值给"fieldName" public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
//只能注解成员变量 @Target(ElementType.FIELD) //运行时注解 @Retention(RetentionPolicy.RUNTIME) //标记注解,被生成javadoc处理 @Documented public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
//父类注解了,子类也跟着被注解了 @Inherited public @interface Greeting { public enum FontColor{ BULE,RED,GREEN}; String name(); FontColor fontColor() default FontColor.GREEN; }
五、自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。六、实例
package com.cn.annotation.test0; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //表明该注解用于注解域(只能对成员变量进行注解,方法和类都不行) @Target(ElementType.FIELD) // 表明该注解为运行时注解,运行时有效,如果RetentionPolicy.CLASS这个注解在编译就会被忽略掉 @Retention(RetentionPolicy.RUNTIME) public @interface CustomizedAnnatation { // 注解名name,默认是"hello world" public String name() default "hello world"; }使用
package com.cn.annotation.test0; import java.lang.reflect.Field; public class TestClass { /** * 姓名 */ @CustomizedAnnatation(name = "RanderC") private String name; /** * 年龄 */ @CustomizedAnnatation(name = "10岁") private String age; /** * 个人介绍 */ @CustomizedAnnatation private String disc; /** * 提示信息 */ private String tips; public static void main(String[] args) { // 得到TestClass.class对象 Class<TestClass> testClass = TestClass.class; // 得到它的所有Fields(成员变量),也就是上面的name和age Field[] fields = testClass.getDeclaredFields(); // 变量所有的Fields(成员变量) for (Field field : fields) { // 设置可见性,如果field设置为private,我们照样可以访问到 field.setAccessible(true); // 判断这个类是否有CustomizedAnnatation注解,明显name、age和都有disc这个注解 if (field.isAnnotationPresent(CustomizedAnnatation.class)) { // 取出这个field的注解对象 CustomizedAnnatation annotation = (CustomizedAnnatation) field .getAnnotation(CustomizedAnnatation.class); // 输出注解名 System.out.println(annotation); // 输出注解的值 System.out.println(annotation.name()); } } } }
输出结果:
@com.cn.annotation.test0.CustomizedAnnatation(name=RanderC) RanderC @com.cn.annotation.test0.CustomizedAnnatation(name=10岁) 10岁 @com.cn.annotation.test0.CustomizedAnnatation(name=hello world) hello world
七、解析Annotation
解析Annotation包括运行时解析和变异是解析,通过上一个例子我们定义的是运行是解析的。method.getAnnotation(AnnotationName.class); method.getAnnotations(); method.isAnnotationPresent(AnnotationName.class);
public static void main(String[] args) { try { Class cls = Class.forName("cn.trinea.java.test.annotation.App"); for (Method method : cls.getMethods()) { MethodInfo methodInfo = method.getAnnotation( MethodInfo.class); if (methodInfo != null) { System.out.println("method name:" + method.getName()); System.out.println("method author:" + methodInfo.author()); System.out.println("method version:" + methodInfo.version()); System.out.println("method date:" + methodInfo.date()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } }2.编译时的解析
@SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" }) public class MethodInfoProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { HashMap<String, String> map = new HashMap<String, String>(); for (TypeElement te : annotations) { for (Element element : env.getElementsAnnotatedWith(te)) { MethodInfo methodInfo = element.getAnnotation(MethodInfo.class); map.put(element.getEnclosingElement().toString(), methodInfo.author()); } } return false; } }SupportedAnnotationTypes 表示这个 Processor 要处理的 Annotation 名字。