【详细】java如何优雅的自定义一个注解?

说到注解,我们一定都不陌生,在我们编码过程中可以说是无处不在,非常的常见。比如:@Override@Deprecated@Controller等等。
那么我们今天来说一下如何自定义一个注解。

一、注解的基本结构

我们在代码里面点开一个注解看一下:

package java.lang;

import java.lang.annotation.*;

/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 *  * <ul><li>
 * The method does override or implement a method declared in a
 * supertype.
 * </li><li>
 * The method has a signature that is override-equivalent to that of
 * any public method declared in {@linkplain Object}.
 * </li></ul>
 *  * @author  Peter von der Ah&eacute;
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这个就是注解的名称:

public @interface Override {
}

这个是注解的元注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)

除了上面这两个以外还有两个元注解。

  • 元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation元注解类型,他们被用来对其他annotation类型做说明。

<1>:@Target :用来描述注解的使用范围,ElementType 枚举选择,可以看到,很多类型有,classmethod等作用域。

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

<2>:@Retention表示需要在什么级别保存到该注解信息,用于描述注解的生命周期,通过RetentionPolicy 枚举进行选择,(SOURCE<CLASS<RUNTIME),默认为RUNTIME

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

<3>:@Document说明该注解被说明在javadoc中

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

<4>:@Inherited:说明子类可以继承父类中的该注解。

主要需要学习一下前两个元注解

二、自定义一个注解

  • 使用@interface来申明一个自定义注解时,他会自动继承ava.lang.annotation.Annotation接口。
  • 格式public @interface xxx(注解名称){定义注解参数内容}。
package annotest;


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

/**
 * 自定义注解
 */
public class Test {
    @myAnnotation
    public void test(){

    };

}
//@Target表示自定义注解的作用范围,METHOD:方法;TYPE:类 ;等等。
@Target({ElementType.METHOD,ElementType.TYPE})
//@Retention自定义注解的 运行作用域
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
    //暂时没有参数

}

上面就定义了一个无参数的自定义注解。如果是注解有参数,需要在注解里面添加参数类型,如下:
在这里插入图片描述
参数格式: 类型 +名称();
可以看到加了参数后,使用注解如果不添加参数的话会报错,除非添加默认值:
在这里插入图片描述
或者
在这里插入图片描述
如果只有一个参数可以直接使用value命名,这样在使用注解的时候可以省略’名称=“xx”'直接赋值

三、注解的使用

自定义注解的使用需要用到反射的原理,直接上代码:

package annotest;

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

public class Test03 {


}
//作用于类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnno{
    String value();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoName{
    String name();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoAge{
    int age();
}

//使用类注解
@ClassAnno("className")
class Student{
    //成员注解
    @FiledAnnoName(name = "张三")
    private String name;
    //成员注解
    @FiledAnnoAge(age = 18)
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

我们在Test03里面定义了三个注解:

//作用于类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnno{
    String value();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoName{
    String name();
}
//作用属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnoAge{
    int age();
}
在Student类里面分别使用了注解。那么我们要如何取到注解里面设置的属性值呢?
 public static void main(String[] args) throws ClassNotFoundException {
        //使用反射获取对量class
        Class aClass = Class.forName("annotest.Student");
        //使用class对象获取 所有类注解
        Annotation[] annotations = aClass.getAnnotations();
        //便利注解信息
        for (Annotation annotation : annotations) {
            System.out.println("类注解:"+annotation);
            //强转为ClassAnno注解对象
            ClassAnno classAnno = (ClassAnno)annotation;

            System.out.println("注解值:"+classAnno.value());
        }

    }

使用反射获取Student注解信息annotations 可以获取到注解里面的值是多少。不清楚的可以去了解一下反射:java进阶之反射

看下结果:

类注解:@annotest.ClassAnno(value=className)
注解值:className

如果想要获取属性变量的注解需要先获取到Filed对象:

 public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //使用反射获取对量class
        Class aClass = Class.forName("annotest.Student");
        //使用class对象获取 所有类注解
        Annotation[] annotations = aClass.getAnnotations();
        //便利注解信息
        for (Annotation annotation : annotations) {
            System.out.println("类注解:"+annotation);
            //强转为ClassAnno注解对象
            ClassAnno classAnno = (ClassAnno)annotation;

            System.out.println("注解值:"+classAnno.value());
        }

        //这里变量是私有的private ,不可以使用getField ,使用getDeclaredField获取Filed
        Field name = aClass.getDeclaredField("name");

        //这里变量是私有的private ,不可以使用getAnnotation ,使用getDeclaredAnnotation获取注解
        FiledAnnoName annotation = name.getDeclaredAnnotation(FiledAnnoName.class);
        String name1 = annotation.name();
        System.out.println("成员注解name值:"+name1);
        System.out.println("------------分割线------------");
        //这里变量是私有的private ,不可以使用getField ,使用getDeclaredField获取Filed
        Field age = aClass.getDeclaredField("age");
        //这里变量是私有的private ,不可以使用getAnnotation ,使用getDeclaredAnnotation获取注解
        FiledAnnoAge annoAge = age.getDeclaredAnnotation(FiledAnnoAge.class);
        int age1 = annoAge.age();
        System.out.println("成员注解age值:"+age1);
    }

需要注意的是,类是默认缺省的,是有访问权限的。而name和age是私有private的,无法直接获取到Filed对象,需要使用getDeclaredField以及getDeclaredAnnotation获取星湖街相关信息,才能够生效。

我们看下结果:

类注解:@annotest.ClassAnno(value=className)
注解值:className
成员注解name值:张三
------------分割线------------
成员注解age值:18

没问题的,好了,自定义注解就说道这里。
加油每一天!奥利给!
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值