Fork me on GitHub

JDK 5.0 注解的使用,自定义注解

了解注解

  在编写代码时,除了源程序以外,我们还会使用Javadoc标签对类、方法或成员变量进行注解,以便使用Javadoc工具生成和源代码配套的Javadoc文档。
/**
* 重写toString
* @param id
* @param name
* @return
*/
public String toString(int id, String name) {
    return id+","+name;
}
  这些@param、@return等javadoc标签就是注解标签,它们为第三方工具提供了描述程序代码的注释信息。
  JDK 5.0注解可以看成是Javadoc标签和Xdoclet标签的延伸和发展。在JDK 5.0中,我们可以自定义这些标签,并通过Java语言的反射机制获取类中标注的注解,完成特定的功能。
  注意,注解是代码的附属信息,就像你对一本好书进行的批注,书的内容并没有因为注解而影响阅读或改变,所以注解遵循一个基本原则:注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能够正常运行。Java语言解释器会忽略这些注解,而由第三方工具负责对注解进行处理。第三方工具可以利用代码中的注解间接控制程序代码的运行,它们通过Java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑,而这正是SpringAOP对@AspectJ提供支持所采取的方法。

一个简单的注解类

  定义注解类本身并不困难,Java提供了定义注解的语法。下面来编写一个栗子🌰:
package com.zhengbin.annotationtest.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Created by zhengbin06 on 2016/10/15.
 */
@Retention(RetentionPolicy.RUNTIME) //①声明注解的保留期限
@Target(ElementType.METHOD) //②声明可以使用该注解的目标类型(这里表明该注解的目标类型是方法,查看@Target的实现,可以看到它的成员为ElementType类型的数组)
public @interface NeedTest { //③定义注解
    boolean internal() default true; //④声明注解成员
    // 注解成员的类型、成员名、与默认值
}
  Java新语法规定使用@interface修饰符定义注解类,如③所示,一个注解类可以有多个成员,成员声明和接口方法声明类似,这里只定义了一个成员,如④所示。成员的声明有一下几点限制:
  • 成员以无入参无抛出异常的方式声明,比如boolean value(String str)、boolean value() throws Exception等方式都是非法的;
  • 可以通过default为成员制定一个默认值,如String level default "LOW_LEVEL"、int hight() default 2都是合法的。当然也可以不制定默认值;
  • 成员类型是受限的,合法的类型包括原始类型及其分装类、String、Class、enums、注解类型,以及上述类型的数组类型。如List foo() 就是非法的。
  成员也可以是注解类型,下面就修改一下NeedTest.java:
  • 添加一个Student.java:
package com.zhengbin.annotationtest.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Created by zhengbin06 on 2016/10/15.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Student {
    String id() default "201370034217";
    String name() default "郑斌";
    int age() default 21;
}
  • 修改NeedTest.java:
@Retention(RetentionPolicy.RUNTIME) // ①声明注解的保留期限
@Target(ElementType.METHOD) // ②声明可以使用该注解的目标类型(这里表明该注解的目标类型是方法,查看@Target的实现,可以看到它的成员为ElementType类型的数组)
public @interface NeedTest { // ③定义注解
    boolean internal() default true; // ④声明注解成员
    // 注解成员的类型、成员名、与默认值
    Student student();
}

  在①和②处,我们所看到的注解是Java预定义的注解,成为元注解(Meta-Annotation),它们被Java编译器使用,会对注解类的行为产生印象。@Retention(RetentionPolicy.RUNTIME)表示NeedTest这个注解可以在运行期被JVM读取,注解的保留期限类型在java.lang.annotation.Retention类中定义,介绍如下:

@Retention,RetentionPolicy

  Target(ElementType.METHOD)表示NeedTest这个注解只能应用到目标类的方法上,注解的应用目标在java.lang.annotation.ElementType类中定义:

@Target,ElementType

  所有的注解类都隐式继承于java.lang.annotation.Annotation,但注解不允许显式继承于其他的接口。

使用注解

  下面我们在ManageService.java中使用注解:

package com.zhengbin.annotationtest.service;
import com.zhengbin.annotationtest.anno.NeedTest;
import com.zhengbin.annotationtest.anno.Student;
/**
 * Created by zhengbin06 on 2016/10/15.
 */
public class ManageService {
    @NeedTest(student = @Student)// ①
    public void accessStudent() {
        System.out.println("中工来了个小学弟!");
    }
    @NeedTest(internal = false, student = @Student(name = "郑斌" , age = 21))// ②
    public void graduateStudent() {
        System.out.println("曾经的小学弟要毕业了。。。");
    }
}
  • 在①处,因为@NeedTest注解类的internal成员有默认值,所以在这里可以不用再进行赋值,但是student成员是一个注解类,所以必须申明出来,但同时也不必赋值,这样使用的就是声明的默认值
  • 在②处,这是完整的声明和赋值

访问注解

  前面提到过,注解不会直接影响程序的运行,但是第三方程序或工具可以利用代码中的注解完成特殊的任务,间接控制程序的运行。对于RetentionPolicy.RUNTIME保留期限的注解,我们可以通过反射机制访问类中的注解。

  在JDK 5.0里,Package、Class、Constructor、Method以及Field等反射对象都新增了访问注解信息的方法:

<T extends Annotation>T getAnnotation(Class<T> annotationClass)

  下面,我们就通过反射来访问注解,得出ManageService类中通过@NeedTest注解所承载的测试需求,如下:

package com.zhengbin.annotationtest.test;
import com.zhengbin.annotationtest.anno.NeedTest;
import com.zhengbin.annotationtest.service.ManageService;
import java.lang.reflect.Method;
/**
 * Created by zhengbin06 on 2016/10/15.
 */
public class TestTool {
    public static void main(String[] args) {
        Class clazz = ManageService.class;
        Method[] methods = clazz.getMethods();
        System.out.println(methods.length);
        for (Method method : methods) {
            NeedTest needTest = method.getAnnotation(NeedTest.class);
            if (needTest != null) {
                if (needTest.internal()) { //
                    System.out.println("小学弟:"+needTest.student().name()); //
                }else {
                    System.out.println("毕业僧:"+needTest.student().name());
                }
            }
        }
    }
}
  • 在①处,判断成员internal的值
  • 在②处,获得成员student在ManageService中的注解信息

  运行以上的代码,输出以下的信息:

11
小学弟:郑斌
毕业僧:郑斌

  方法个数为11,除了我们申明的两个,其它都是父类Object的方法。

posted @ 2016-10-16 12:17  郑斌blog  阅读(674)  评论(1编辑  收藏  举报