1. 元注解 (meta-annotation)
在Java 1.5以后,jdk提供了三个标准注解,定义在java.lang中:
@Override: 用于修饰此方法覆盖了父类的方法
@Deprecated: 用于修饰已经过时的方法
@SuppressWarnnings: 用于通知java编译器禁止特定的编译警告
而元注解的作用,就是负责注解其他注解的,包括上面三个java自带的注解,也包括用户自定义的注解类型。Java 1.5中有4个标准的元注解,被用来提供对其它Annotation类型作说明:
@Target
@Targer说明了注解所修饰的对象范围,即被描述的注解可以用在什么地方,取值有:
使用实例:
@Target(ElementType.TYPE) public @interface Table { /** * 数据表名称注解,默认值为类名称 * @return */ public String tableName() default "className"; } @Target(ElementType.FIELD) public @interface NoDBColumn { }
@Retention
@Retention定义了该注解被保留的时间长短,即表示被描述的注解在什么范围内有效,用于描述注解的生命周期。取值有:
实例如下:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
2. 自定义注解
使用@interface自定义注解时,自动实现了java.lang.annotation.Annotation接口,由编译器自动完成其他细节。在定义注解时,不能继承其他注解或接口。在注解定义体中,每一个方法实际上是声明了一个配置参数,方法名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是Class、String、enum、基本类型),可以通过default来声明参数的默认值。
如果只有一个参数成员,最好把参数名设为value。
实例如下:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果名称注解 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FruitName { String value() default ""; }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果颜色注解 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FruitColor { /** * 水果颜色枚举 */ public enum Color { BLUE, RED, GREEN }; /** * 颜色属性 */ Color fruitColor() default Color.GREEN; }
public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor = Color.RED) private String appleColor; @FruitProvider(id = 1, name = "北京平谷大桃集团", address = "北京市平谷区") private String appleProvider; //省略setter和getter方法 }
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null,因此,使用空字符串或者0或者-1作为默认值是一种常用的做法。
/** * 水果供应商注解 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FruitProvider { /** * 供应商编号 */ public int id() default -1; /** * 供应商名称 */ public String name() default ""; /** * 供应商地址 */ public String address() default ""; }
3. 注解处理器
如果没有用来读取注解的方法,那么注解也就没什么用了。使用注解的过程中,很重要的一部分就是创建和使用注解处理器。Java 1.5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。
注解处理器类库java.lang.reflect.AnnotatedElement,这是一个接口,它的实现类有AccessibleObject, Class, Constructor, Field, Method, Package,所以程序通过反射获取了某个类的AnnotatedElement对象以后,程序就可以通过调用该对象以下四个方法来方位Annotation信息:
一个简单的注解处理器:
public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz) { String strFruitName = "水果名称:"; String strFruitColor = "水果颜色:"; String strFruitProvider = "供应商信息:"; Field[] fields = clazz.getDeclaredFields(); //遍历fields for (Field field : fields) { if (field.isAnnotationPresent(FruitName.class)) {//如果有FruitName注解 //获取注解 FruitName fruitName = field.getAnnotation(FruitName.class); //拿到注解的值并打印 strFruitName += fruitName.value(); System.out.println(strFruitName); } else if (field.isAnnotationPresent(FruitColor.class)) {//如果有FruitColor注解 FruitColor fruitColor = field.getAnnotation(FruitColor.class); strFruitColor += fruitColor.fruitColor().toString(); System.out.println(strFruitColor); } else if (field.isAnnotationPresent(FruitProvider.class)) {//如果有FruitProvider注解 FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class); strFruitProvider += "供应商编号:" + fruitProvider.id() + " 供应商名称:" + fruitProvider.name() + " 供应商地址:" + fruitProvider.address(); System.out.println(strFruitProvider); } } } }
public class FruitRun { public static void main(String[] args) { FruitInfoUtil.getFruitInfo(Apple.class); } } 水果名称:Apple 水果颜色:RED 供应商信息:供应商编号:1 供应商名称:北京平谷大桃集团 供应商地址:北京市平谷区