java注解解析

Posted by 梧桐和风的博客 on April 22, 2017

java注解解析

什么是注解

什么是注解,注解就是一种描述源码的元数据。我们可以通过注解给类、方法或字段提供额外的信息以便了解更多信息。

举个例子,java中常见的@Override就是一个注解。它的作用是提示由它修饰的方法是一个重写方法,如果父类没有这个方法编译器会报错。这样这个注解就给我们传达了重写方法这个信息,在使用时就会多加注意。

  @Override
    public String toString(){
        return "this is my string implements";
  }

又如,在Spring体系中使用的注解,如ServiceController等,告知Spring这是一个bean,并进行相应处理。

怎样自定义注解

了解了注解的含义和用途,下面就该编写自己的注解了。

在深入之前,先要知道4种元注解,这4种注解用于定义修饰其他注解。

  1. @Target 描述注解用于哪里,如类、方法或是字段。
  2. @Retention 指明注解的声明周期
  3. @Documented 注解是否包含在javaDoc中
  4. @Inherited 是否允许子类继承该注解

@Documented@Inherited好理解一些,下面我们看看前两个注解:

@Target

表明该注解是用于修饰类还是方法或字段。取值是一个ElementType枚举类。主要有以下值:

ElementType.TYPE  //描述类、接口或enum声明
ElementType.FIELD  //描述实例变量
ElementType.METHOD  //方法
ElementType.PARAMETER  //参数
ElementType.CONSTRUCTOR  //构造函数
ElementType.LOCAL_VARIABLE  //本地变量
ElementType.ANNOTATION_TYPE  //另一个注释
ElementType.PACKAGE //用于记录java文件的package信息

@Retention

定义该注解信息在什么期间有效,取值为RetentionPolicy枚举类,共有3个值:

RetentionPolicy.SOURCE
在编译期就丢弃,仅存在于源码中。如@Override等就属于此类

RetentionPolicy.CLASS 在类加载期丢弃,即注解将存在于字节码中。默认注解为此类型

RetentionPolicy.RUNTIME 注解信息一直保留,不会丢弃。这意味着可以反射获取到注解信息。一般自定义注解大都使用此方式。

注解定义

注解使用以下@interface关键字定义:如定义一个名为DAO的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DAO {
}

由上可知,该@DAO用于修饰类、接口或枚举类,注解信息一直存在不会擦除。

下面再自定义一个名为Hello注解,用于修饰方法,可生产在javaDoc中,可通过反射拿到信息的注解。


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Hello {
    String value() default "Hi !";
}

上面注解体内添加了value方法。并指定了默认值。表明该注解的一个属性,使用时可以这样:

    @Hello(value = "world")
    public String hello() {
        return "hello";
    }

当然也可在注解内添加多个方法,对应使用时的多个属性。这里就不提了。

让注解起作用

看过前面的定义和使用,你可能会疑惑,我没有定义任何逻辑,注解怎么起作用啊?是啊,我们知道,@Override是java的注解,它靠编译期检查父类方法就能实现,而我们自定义的注解怎么办呢?

事实上,定义注解只是编写注解的第一步。我们需要在合适的时机给她赋予逻辑。还记得前面提过,自定义的注解大都是RetentionPolicy.RUNTIME类型的。这是为了在反射时取得注解信息。下面是一个小例子。


public class TestAnno {

    @Hello(value = "world")
    public String hello() {
        return "hello";
    }

    public void callHello()  {
        try {
           TestAnno testAnno = new TestAnno();
            Method method = testAnno.getClass().getMethod("hello", null);
            Hello todo = method.getAnnotation(Hello.class);  //获取注解信息
            String str = method.invoke(testAnno, null).toString();
            System.out.println(str + "," +todo.value() );  //将注解信息用在方法中
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        TestAnno testAnno = new TestAnno();
        testAnno.callHello();
    }
}


打印结果 :

hello,world

以上只是一个小小的例子,并没有使用价值。但能说明可以在运行时获取注解信息并在方法运行时相结合。其他地方也是用了反射,大同小异。