Java 注解Annotation研究

611 阅读5分钟

目前主流框架都支持注解。比如Android平台上的 Retrofit,EventBus,Butterknife等
Java Web: Spring MVC ,MyBatis等.注解是从Java SDK 1.5 开始启动使用的,使用注解到底有什么好处呢?

对于配置文件需要添加较多逻辑代码来处理,而注解只需要注解元 数据就可以区分,代码整洁,阅读性好,减少配置文件等。

annotation.png
annotation.png

如上图所示:
Java的注解分为元注解和标准注解。

  • 标准注解:系统提供的注解

    @Deprecated :表示已经过期,不推荐使用.比如你有一个方法现在不想用了,但是其他人调用了,删掉会影响其他代码。这样用这个标记来说明.

    @Documented
    @Retention(RetentionPolicy.RUNTIME)//运行期runtime
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//范围
    public @interface Deprecated {
    }
    @Deprecated
    public void test(){
    }

@Override 主要作用是覆盖父类方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)//在源文件中出现,在编译后会被编译器丢弃
public @interface Override {
}

比如android onCreate方法,覆盖父类方法

 @Override
 protected void onCreate(Bundle savedInstanceState) {
}

比如java toString()方法,覆盖java lang toString方法

 @Override
  public String toString() {
        return super.toString();
   }

@SupressWarning 关闭不当的编译器警告信息
比如

  • 元注解:可以自己定义注解

    @Retention 表示需要在什么级别保存该注解信息,标记有下面3个参数可选
    ```
    public enum RetentionPolicy {
    /**

    • Annotations are to be discarded by the compiler.
      */
      SOURCE,//注解将被编译器丢弃

    /**

    • Annotations are to be recorded in the class file by the compiler
    • but need not be retained by the VM at run time. This is the default
    • behavior.
      */
      CLASS,//注解在class文件可用,但是会VM丢弃

      /**

    • Annotations are to be recorded in the class file by the compiler and
    • retained by the VM at run time, so they may be read reflectively.
      *
    • @see java.lang.reflect.AnnotatedElement
      */
      RUNTIME//VM运行期也会保留注解,可用用反射机制可用读取对应的数据
      }
      >@Target 表示被描述的注解用于什么地方,可用运用到地方如下
      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.ANNOTATION_TYPE)
      public @interface Target {
      /**
    • Returns an array of the kinds of elements an annotation type
    • can be applied to.
    • @return an array of the kinds of elements an annotation type
    • can be applied to
      */
      ElementType[] value();//这边是一个ElementType数组
      }

public enum ElementType {
/* Class, interface (including annotation type), or enum declaration /
TYPE,//用于描述类,接口 或枚举声明

/** Field declaration (includes enum constants) */
FIELD,//用于描述域

/** Method declaration */
METHOD,//用于描述方法

/** Formal parameter declaration */
PARAMETER,//用于描述参数

/** Constructor declaration */
CONSTRUCTOR,//用于描述构造器

/** Local variable declaration */
LOCAL_VARIABLE,//用于描述局部变量

/** Annotation type declaration */
ANNOTATION_TYPE,//用于描述Annotation type

/** Package declaration */
PACKAGE,//描述包

/**
 * Type parameter declaration
 *
 * @since 1.8
 */
TYPE_PARAMETER,

/**
 * Use of a type
 *
 * @since 1.8
 */
TYPE_USE

}


>@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

>@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的,则这个annotation将被用于该class的子类。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

自定义自己的Annotation 格式如下
public @interface 注解名 {定义体}

定义体里面一般如下图所示:

![anntion2.png](http://upload-images.jianshu.io/upload_images/2155810-4b26c1633260ed4b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

举个栗子写个Demo吧:
定义一个Person Annotation 里面定义了2个方法 say 和move 方法.

@Target({ElementType.METHOD,ElementType.TYPE,ElementType.LOCAL_VARIABLE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonAnnotation {
public String say() default "hello";
public String move() default "walk";
Sex sex() default Sex.MAN;//枚举类型
}

定义一个UserAnnotation 定义name, age 和 PersonAnnotation annotation

@Target({ElementType.METHOD , ElementType.TYPE,ElementType.FIELD,ElementType.LOCAL_VARIABLE})//可以修饰方法,实例变量,local 变量
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
String name() default "";
String age() default "";
PersonAnnotation person();//使用了Annotation类型
}

定义性别enum.

public enum Sex {
MAN,WOMEN,OTHER
}

上面的代码定义的不错,那怎么使用呢?答案是反射,直接上代码吧😆
定义User类

@UserAnnotation//因为在Target定义了ElementType.TYPE可以修饰类
public class User {
@UserAnnotation(name = "Star")//因为在Target中定义了ElementType.FIELD就可以定义在实例变量中拉
protected String name;
@UserAnnotation(age = 100)
int age;
@UserAnnotation(name = "wong" , age = 1000)
protected String value;
@UserAnnotation(name = "GEAK",age = 90)//因为在Target中定义了ElementType.METHOD
public void getUserInfo(String name,int age){
System.out.println("the name is:"+name+"\tthe age is:"+age);
}
public void getInfo(){
System.out.println("=======getInfo start=====");
System.out.println("the name is:"+this.name+"\tthe age is:"+this.age);
System.out.println("=======getInfo end=====");
}

}

定义了自己的元数据,下面是测试怎么使用
@Test
public void test() {
    //User user = new User();
    System.out.println("startTest");
    Class<User> u = User.class;
    try {
        Method method = u.getMethod("getUserInfo", new Class[]{String.class, int.class});
        UserAnnotation userAnnotation = method.getAnnotation(UserAnnotation.class);
        System.out.println("the name is:" + userAnnotation.name() + "the age is:" + userAnnotation.age());
        if (method != null) {
            method.invoke(u.newInstance(), userAnnotation.name(), userAnnotation.age());
        }
        Field fields[] = u.getDeclaredFields();
        Field f = u.getDeclaredField("value");
        UserAnnotation userAnnotation1 = f.getAnnotation(UserAnnotation.class);
        System.out.println("the f name is:" + userAnnotation1.name() + "the f age is:" + userAnnotation1.age());
        for (Field field : fields) {
            if (field.isAnnotationPresent(UserAnnotation.class)) {
                System.out.println("the name is:" + field.getName());
                UserAnnotation annotation = field.getAnnotation(UserAnnotation.class);
                if (annotation != null) {
                    System.out.println("the name is:" + annotation.name() + "the age is:" + annotation.age());
                }
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

}
获取对应的User Class ,有下面两种方式
- 获取Class相关
1.Class<User> u = User.class; //通过Class直接获取
2.Class<User> u = Class.forName("User");//通过name获取

- Class中主要用到的方法
Method[] getMethods() //获取所有的方法包括父类的方法
Field[] getFields() //获取所有实例变量包括父类的实例
Field[] getDeclaredFields()//获取所有的方法不包括父类的方法
Method[] getDeclaredMethods()//获取所有实例变量不包括父类的实例
- 获取Annotation
Method.getAnnotation(Annatation.class)//获取方法上面的Annotation
Field.getAnnotation(Annatation.class)//获取filed上的Annotation

获取结果:

the name is:GEAKthe age is:90 //获取到方法上的Annotation
the name is:GEAK the age is:90//获取到方法上的Annotation
the f name is:wongthe f age is:1000//利用反射的机制来调用方法
the name is:name
the name is:Starthe age is:10//获取对应实例的name上的Annotation
the name is:age
the name is:the age is:100
the name is:value
the name is:wongthe age is:1000
```
总结:
通过Annotation和反射机制来进行合理的配置你的源代码.这样就可以省去大部分的if else或者初始化等工作.