简介

  • annotation是Java5引入的新特征,它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)关联。
  • 通俗来讲,就是为程序的元素加上更直观明了的说明,这些说明信息是与程序的业务逻辑无关,提供给指定的工具或框架使用的。
  • annotation实际是一种接口,通过Java反射机制来访问annotation信息,并根据这些信息来决定如何使用被注解的程序元素或改变其行为。

  • annotation类型使用关键字@interface定义,表示继承了java.lang.annotation.Annotation接口,并声明它也是一个接口。

  • annotation与接口相似,可以定义常量、静态成员类型(如枚举类)。
  • annotation类型的方法必须是、声明为无参数、无异常抛出的。
  • 成员方法即为成员属性,方法名为属性名,方法返回值为属性类型。
  • 方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或由这些类型之一作为元素的数组。方法后面可以使用default和一个默认值来声明成员的默认值(null除外)。

元注解

  • 作用:注解其他注解。
  • Java5定义了4个标准的meta-annotation类型,用于对其他annotation作说明。
    • @Target
    • @Retention
    • @Documented
    • @Inherited

@Target

  • 说明了Annotation所修饰的对象范围,也即是注解的使用范围。annotation可悲用于packages、types(类、接口、枚举、annotation类型)、类型成员(方法、属性、枚举值)、方法参数和本地变量(如循环变量、catch参数),使用target可明确其修饰的目标。
  • 取值(ElementType):

    • CONSTRUCTOR:注解在构造器上。
    • FIELD:成员属性。
    • LOCAL_VARIABLE:局部变量。
    • METHOD:方法。
    • PACKAGE:包。
    • PARAMETER:参数。
    • TYPE:type。
  • 实例:

//数据表名注解
@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 表名,默认值为被注解类名
     * @return
     */
    public String tableName() default "className";
}

//不映射字段注解
@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

@Retention

  • 作用:定义了注解被保留的时间长短。某些注解只出现在源代码中,不被编译,一些则被编译到class文件中,但它们可能会被虚拟机忽略,另一些则在加载Class时被读取。使用此元注解可对注解的生命周期进行限制,指明需要在什么级别保存该注解。
  • 取值(RetentionPolicy):

    • SOURCE: 在源文件中有效(即保留在源文件)
    • CLASS:在class文件中有效。
    • RUNTIME:在运行时有效。
  • 实例:

@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;
}
  • RetentionPolicy取值为RUNTIME,这样注解处理器可以通过反射,获取该注解的属性值,从而去做一些运行时的逻辑处理。

@Documented

  • 作用:使annotation被javadoc类的工具文档化。
  • 没有取值可选,直接加在annotation上即可。

@Inherited

  • 作用:表示被annotation注解的类其子类也继承了此annotation,但实现了带此annotation的接口的实现类并不会继承该annotation,方法也不会从它重载的方法继承annotation。
  • 无取值可选。

自定义注解

  • 使用@interface自定义注解,注意不能继承其他的注解或接口。
  • 其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,返回值类型就是参数类型。
  • annotation里面的参数要求:
  1. 只能用public或default修饰,如String value()即为default。
  2. 类型只能是基本数据类型、String、Enum、Class、Annotation和它们之一的数组。
  3. 如果只有一个参数成员,最好命名为value,则可以直接注入值。
  • 实例:
 /**
  * 水果名称注解
  * @author wangsheng
  *
  */
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface FruitName {
     String value() default "";
 }

 /**
  * 水果颜色注解
  * @author peida
  *
  */
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface FruitColor {
     //颜色枚举
     public enum Color{BULE,RED,GREEN};

     //颜色属性
     Color fruitColor() default Color.GREEN;
 }

//使用注解
 public class Apple {
     @FruitName("Apple") //直接注入值而不用指明属性
     private String appleName;

     @FruitColor(fruitColor=Color.RED)
     private String appleColor;
 }
  • 非基本类型的注解属性的值不可为null,因此,使用空字符串和0作为默认值是一种常用的做法。也正是因为不能为null,处理器很难判断一个属性是否缺失,所以我们只能定义一些特殊的默认值,例如空字符串或负数来表示某个属性不存在。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;

    /**
     * 供应商名称
     * @return
     */
    public String name() default "";
}
  • 实例:最后还模拟了一下反射机制对注解的解析。
/** 
 * 定义作者信息的注解,name和group 
 */  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@Documented  
public @interface Author {  
    String name(); 
    String group();  
}

/** 
 * 定义描述信息的注解,value 
 */  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)  
@Documented  
public @interface Description {  
    String value();
}

/**
 * 使用注解
 */
@Description(value="这是一个有用的工具类") // value可以省略
public class Utility {

    @Author(name="wangsheng", group="developer team")
    public String work() {
        return "work over!";
    }

}

/**
 *在运行时分析处理annotation类型的信息
 */
public class AnalysisAnnotation {

    public static void main(String[] args) {
        try {

            // 通过运行时反射API获得annotation信息
            Class<?> rtClass = 
                Class.forName("com.wsheng.aggregator.annotation.Utility");
            Method[] methods = rtClass.getMethods();

            //判断Description注解是否存在
            boolean descriptionExist = 
                rtClass.isAnnotationPresent(Description.class);
            if (descriptionExist) {
                //从类中获取注解Description
                Description description = 
                    (Description)rtClass.getAnnotation(Description.class);
                //Utility's Description --- > 这是一个有用的工具类
                System.out.println("Utility's Description --- > " 
                    + description.value()); 

                //从方法中获取Author注解
                for (Method method : methods) {
                    if (method.isAnnotationPresent(Author.class)) {
                        Author author = (Author)method.getAnnotation(Author.class);
                        //Utility's Author ---> wangsheng from developer team
                        System.out.println("Utility's Author ---> " + author.name() 
                            + " from " + author.group());
                    }
                }

            }

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

}

自定义的注解使用其他已定义注解

  • 有的时候要在自定义的注解上使用其他定义好的注解,比如:
@Caching(
    put = {
            @CachePut(value = "user", key = "#user.id"),
            @CachePut(value = "user", key = "#user.username"),
            @CachePut(value = "user", key = "#user.email")
    }
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}
  • 把要用的注解取好值加在自定义注解上,然后这个自定义注解加在方法上自然就会拥有@Caching的功能,不过属性就不能改了。
  • 详见笔记:Spring Cache

  • 参考文章