注解的作用
- 生成文档, 这是java最常见的,也是java最早的注解。
- 跟踪代码依赖性,实现替代配置文件功能。
- 在编译时进行严格检查
- 如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
实现一个简单的注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTarget {
}
上面是一个最简单的注解实现,没有定义任何的属性,其中需要注意的是@Retention(RetentionPolicy.RUNTIME)是定义注解所必须的。
@Retention是注解的注解,称为注解的元注解。括号里有一个枚举类型的值,即为注解内部定义的值。打开Retention的实现:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
可以看到这里定义了一个变量value并且没有缺省值,所以不写这个value就会报错。 继续打开RetentionPolicy:
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
可以发现这个枚举类定义了三个值,这三个值分别代表的是我们定义的MyTarget如何保持。
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得 @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
还需要注意的是java的元注解一共有四个:
@Document
@Target
@Retention
@Inherited
@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) ///包
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
我们实现了一个最简单的注解MyTarget,下面进行简单的测试:
@MyTarget
public void doSomething() { }
@Resource
public static void main(String[] args) throws Exception
{
Method method = MyTargetTest.class.getMethod("doSomething",null);
if(method.isAnnotationPresent(MyTarget.class))
{ System.out.println(method.getAnnotation(MyTarget.class));
}
}
运行上面的测试会打出一句话:
@annotations.MyTarget()
其中if(method.isAnnotationPresent(MyTarget.class))
可以判断出方法是是不是存在MyTarget的注解,能打印出这句话证明我们写的确实是一个注解。
注解中的属性定义
上面的内容我们实现了一个最简单的注解,但是这个注解基本是没有什么功能的,java注解的功能实现基本是通过定义属性实现的(真正实现功能有相关的处理类,处置这些属性,我们先来定义属性)。
注解处理器类库(java.lang.reflect.AnnotatedElement):AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
- 方法1: T getAnnotation(Class annotationClass): 返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
- 方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
- 方法3:boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
- 方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
注解定义属性不同于java,看一下上面的Retention的定义属性方式:
RetentionPolicy value();
这基本就是注解定义属性的方式,类似于java中定义方法,可以设置缺省值,即用注解的时候缺省值是可以不写的。
我们来看一下注解的功能之一: 为属性赋值,我们定义三个注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface CarSalerTarget {
public String salerName();
public int age() default 1;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface CarNameTarget {
String name() default "";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface CarSalerTarget {
public String salerName();
public int age() default 1;
}
都是汽车相关的信息,中文名,英文名,销售员信息; 下面我们定义一个奔驰汽车的类,试着用注解进行赋值:
public class BnechCar {
@CarNameTarget(name="奔驰")
private String name;
@CarTypeTarget(carType= CarTypeTarget.Type.benchi)
private String type;
@CarSalerTarget(salerName="wpz",age=22)
private String salerInfo;
}
这样其实就定义和赋值完成了,我们搞一个中间类来打印一下信息:
public class CarInfoUtil {
public static void getFruitInfo(Class<?> clazz){
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(CarNameTarget.class)){
CarNameTarget name = (CarNameTarget) field.getAnnotation(CarNameTarget.class);
String carColour ="汽车的中文名:"+name.name();
System.out.println(carColour);
}
else if(field.isAnnotationPresent(CarTypeTarget.class)){
CarTypeTarget type= (CarTypeTarget) field.getAnnotation(CarTypeTarget.class);
String carType="汽车的类型:"+type.carType().toString();
System.out.println(carType);
}
else if(field.isAnnotationPresent(CarSalerTarget.class)){
CarSalerTarget saler= (CarSalerTarget) field.getAnnotation(CarSalerTarget.class);
String salerInfo=" 销售员姓名:"+saler.salerName()+" 销售员年龄:"+ saler.age();
System.out.println(salerInfo);
}
}
}
}
这个类其实就是根据注解获取属性的值。
@Test
public void testTarget(){
CarInfoUtil.getFruitInfo(BnechCar.class);
}