Java自定义注解入门

1. 元注解 (meta-annotation)

在Java 1.5以后,jdk提供了三个标准注解,定义在java.lang中:

    @Override: 用于修饰此方法覆盖了父类的方法

    @Deprecated: 用于修饰已经过时的方法

    @SuppressWarnnings: 用于通知java编译器禁止特定的编译警告

而元注解的作用,就是负责注解其他注解的,包括上面三个java自带的注解,也包括用户自定义的注解类型。Java 1.5中有4个标准的元注解,被用来提供对其它Annotation类型作说明:

  • @Target
  • @Retention
  • @Documented
  • @Inherited

@Target

@Targer说明了注解所修饰的对象范围,即被描述的注解可以用在什么地方,取值有:

  • CONSTRUCTOR:用于描述构造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部变量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述参数
  • TYPE:用于描述类、接口(包括注解类型) 或enum声明

使用实例:

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

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

@Table可以用于注解类、接口或enum声明,而@NoDBColumn只能用于注解类的成员变量。


@Retention

@Retention定义了该注解被保留的时间长短,即表示被描述的注解在什么范围内有效,用于描述注解的生命周期。取值有:

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在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;
}

@Column注解的Retention取值是RUNTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时逻辑处理。


2. 自定义注解

使用@interface自定义注解时,自动实现了java.lang.annotation.Annotation接口,由编译器自动完成其他细节。在定义注解时,不能继承其他注解或接口。在注解定义体中,每一个方法实际上是声明了一个配置参数,方法名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是Class、String、enum、基本类型),可以通过default来声明参数的默认值。

如果只有一个参数成员,最好把参数名设为value。


实例如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果名称注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
    String value() default "";
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果颜色注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitColor {

    /**
     * 水果颜色枚举
     */
    public enum Color {
        BLUE, RED, GREEN
    };

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


public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor = Color.RED)
    private String appleColor;

    @FruitProvider(id = 1, name = "北京平谷大桃集团", address = "北京市平谷区")
    private String appleProvider;
    //省略setter和getter方法
}

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null,因此,使用空字符串或者0或者-1作为默认值是一种常用的做法。

/**
 * 水果供应商注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitProvider {

    /**
     * 供应商编号
     */
    public int id() default -1;

    /**
     * 供应商名称
     */
    public String name() default "";

    /**
     * 供应商地址
     */
    public String address() default "";
}


那么如何响应注解信息处理流程呢?下面将学习注解信息的获取和处理。


3. 注解处理器

如果没有用来读取注解的方法,那么注解也就没什么用了。使用注解的过程中,很重要的一部分就是创建和使用注解处理器。Java 1.5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。

注解处理器类库java.lang.reflect.AnnotatedElement,这是一个接口,它的实现类有AccessibleObject, Class, Constructor, Field, Method, Package,所以程序通过反射获取了某个类的AnnotatedElement对象以后,程序就可以通过调用该对象以下四个方法来方位Annotation信息:

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass):返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
  • Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
  • boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
  • Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

一个简单的注解处理器:

public class FruitInfoUtil {

    public static void getFruitInfo(Class<?> clazz) {
        String strFruitName = "水果名称:";
        String strFruitColor = "水果颜色:";
        String strFruitProvider = "供应商信息:";

        Field[] fields = clazz.getDeclaredFields();

        //遍历fields
        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitName.class)) {//如果有FruitName注解
                //获取注解
                FruitName fruitName = field.getAnnotation(FruitName.class);
                //拿到注解的值并打印
                strFruitName += fruitName.value();
                System.out.println(strFruitName);
            } else if (field.isAnnotationPresent(FruitColor.class)) {//如果有FruitColor注解
                FruitColor fruitColor = field.getAnnotation(FruitColor.class);
                strFruitColor += fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            } else if (field.isAnnotationPresent(FruitProvider.class)) {//如果有FruitProvider注解
                FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
                strFruitProvider += "供应商编号:" + fruitProvider.id() + " 供应商名称:"
                                    + fruitProvider.name() + " 供应商地址:" + fruitProvider.address();
                System.out.println(strFruitProvider);
            }
        }

    }
}

运行以后,结果是这样的:

public class FruitRun {

    public static void main(String[] args) {

        FruitInfoUtil.getFruitInfo(Apple.class);

    }

}

水果名称:Apple
水果颜色:RED
供应商信息:供应商编号:1 供应商名称:北京平谷大桃集团 供应商地址:北京市平谷区

这里贴一张Java注解的基础知识点导图, 图片地址




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值