[Java]自定义注解

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/17993690
出自【进步*于辰的博客

由于单独的一个或多个元注解无法进行测试,故本篇文章中的示例都是基于自定义注解。因此,大家在阅读代码时,可能会觉得有点云里雾里。无妨,疑惑是暂时的。

启发博文:《自定义注解详细介绍》(转发)。
参考笔记一,P72.1、P76.1。

注: 本篇文章引入了两个知识点,会在举例时使用。

  1. 反射,详述可查阅博文《[Java]反射》;
  2. JavaDoc文档,推荐一篇博文《【Java学习笔记】【基础篇】07.JavaDoc以及两种使用方式》(转发)。

1、四个元注解

1.1 @target

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

指定所标注的注解可标注于哪些java元素(如:类、属性、方法等),value的取值可自行查看源码。

在不指定value时,表示所标注的注解可标注于任何java元素。此时,会根据所标注的注解所标注的元素类型自适应具体的ElementType

示例:

@Target({ElementType.TYPE, ElementType.FIELD})// 可用于注解类和属性
@interface SelfAnno {}

@SelfAnno// 通过
class TestAnno {
    @SelfAnno// 通过
    private Integer id;

    @SelfAnno // 报错
    public void test() {}
}

1.2 @Retention

源码:

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

指定所标注的注解的生命周期value有三个取值:SOURCECLASSRUNTIME,默认为CLASS

值说明:

  1. SOURCE:表示所标注的注解只保留在 java 源文件(xx.java)中,JVM不会进行编译。
  2. CLASS:表示所标注的注解会被编译到 class 字节码文件(xx.class)中,但JVM会忽略,即无法获取。
  3. RUNTIME:表示所标注的注解会被编译到JVM中,可通过反射获取,实际开发中的自定义注解的生命周期几乎都是这个。

1.3 @Documented

源码:

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

指定所标注的注解将跟随java文件到JavaDoc文档中。

1.4 @Inherited

源码:

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

指定所标注的注解是否可被所标注的类的子类继承此注解。

示例。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface SelfAnno {
    String sign();
}

class TestAnno {
    @SelfAnno(sign = "type")
    class C {
        @SelfAnno(sign = "method")
        public void test() {}
    }

    class SubC extends C {}
    
    public static void main(String[] args) throws Exception {
        Class clazz  = C.class;
        sout getSign(clazz.getAnnotation(SelfAnno.class));// type
        sout getSign(clazz.getMethod("test").getAnnotation(SelfAnno.class));// method

        clazz = SubC.class;
        sout getSign(clazz.getAnnotation(SelfAnno.class));// type
        sout getSign(clazz.getMethod("test").getAnnotation(SelfAnno.class));// method
    }

    private static String getSign(Annotation anno) {
        return ((SelfAnno)anno).sign();
    }
}

标注于类、或者方法上的注解都可以被继承。

2、自定义注解

2.1 介绍

所有注解都自动继承java.lang.annotation.Annotation接口,注解由@interface声明,上述源码中的value()是注解元素,类似于成员属性。

注解元素必须由()结尾,可以使用default定义默认值,使用注解时必须为所有无默认值的注解元素赋值。

举个例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface SelfAnno {
	String platform() default "博客平台";
    String name();
}

@SelfAnno(platform = "云平台", name = "CSDN")// platform = "云平台" 可省略
class Csdn {}

注意

  1. 注解元素必须由public修饰,默认是public
  2. 一般注解元素以名词命名,若只有一个,建议名称为value
  3. 注解元素类型只能是基本数据类型、基本数据类型数组或注解类型(注解嵌套);
  4. default指定注解元素默认值时,值类型必须与注解元素类型相同。

2.2 使用细节

  1. 若无注解元素,可省略()(小括号);
  2. 若注解元素类型为数组,且赋值时只有一个值时,可以省略{}(花括号);
  3. 如果只有一个注解元素,且注解元素名为value。无论其是什么类型,都可省略前缀xx = )。
  4. ElementTypePACKAGE,则此注解用于标注在package-info.java文件中。:这个文件默认是不创建的。在idea中,双击shift可搜索到,打开时才会创建。其用途尚未可知,而不是类文件(xx.java)的第一行的package...上。

3、@AliasFor

Spring提供的这个注解为注解的使用带来了很大的便利。

启发博文:《详解@AliasFor注解的使用与注意事项》(转发)。

这篇文章说明得很详细,我便不赘述,仅作两点小结:

  1. 注解内:成对存在且相互映射,返回值类型与默认值必须相同。标注时若都指定,值必须相同。
  2. 注解间:需先标注,返回值类型必须相同。

最后

本文中的示例是为了方便大家理解自定义注解的定义和使用方法而简单举出的,不一定有实用性,旨在抛砖引玉。大家阅读完后,可能仍有疑惑,不妨自行测试一下,就都理解了。

另外,自定义注解时可使用“静态导入”(import static),会更简便。

本文完结。

posted @ 2024-01-29 01:04  进步·于辰  阅读(101)  评论(0编辑  收藏  举报