如何自定义一个Java注解?

Java注解(Annotation)是JDK1.5引入的一种新特性,可以标注在类、方法、变量、参数上,官方定义如下:

Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的

一般官方定义都给的晦涩难懂,其实我们简单理解一下,注解就是标注在类、方法、变量、参数上的一种标签,你可以使用JDK内置的注解,也可以自定义注解,通常情况下我们都是使用自定义注解来完成自己的注解功能

注解分为:JDK内置的注解和元注解

JDK内置的注解

1、@Override

只要学过Java面向对象特性的人对于这个注解应该都很熟悉,@Override用来标注子类覆盖父类的方法,当标注了这个注解后父类中的方法有变更,编译器会给出错误提示

2、 @Deprecated

此注解主要是用来标识一些方法已经过时了,不推荐使用了

3、@SuppressWarnings

这个注解用于告诉编译器忽略特定的警告信息,当你不希望编译器在你的方法上有警告信息时,可以通过@SuppressWarnings注解来消除警告信息

4、@SafeVarargs

Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告

5、@FunctionalInterface

Java 8 开始支持,标识一个匿名函数或函数式接口

使用示例

public class AnnotationTest extends HashMap {

    @Override
    public Object put(Object key, Object value) {
        return super.put(key, value);
    }

    @Deprecated
    public String testDeprecated(){
        return "Deprecated";
    }

    @SuppressWarnings("uncheck")
    public String testSuppressWarnings(){
        return "SuppressWarnings";
    }
    
}
public class SafeVarargsTest<T> {

    private T[] params;

    //构造函数可以使用@SafeVarargs
    @SafeVarargs
    public SafeVarargsTest(T... params){
        this.params = params;
    }

    //普通方法不能使用@SafeVarargs,如果要忽略警告信息可以使用@SuppressWarnings("uncheck")
    //@SafeVarargs
    @SuppressWarnings("uncheck")
    public void normalMethod(T... params){
        for(T t : params){
            System.out.println(t);
        }
    }

    //static方法可以使用@SafeVarargs
    @SafeVarargs
    public static<T> void staticMethod(T... params){
        for(T t : params){
            System.out.println(t);
        }
    }

    //final方法可以使用@SafeVarargs
    @SafeVarargs
    public final void finalMethod(T... params){
        for(T t : params){
            System.out.println(t);
        }
    }


}
/**
 * 函数式接口:有且仅有一个抽象方法
 * @FunctionalInterface 是用来标注函数式接口的,其实不用@FunctionalInterface标注也可以表示函数式接口
 * 只是加了注解就可以从编译器的层面来限制只能包含一个抽象方法,超过一个抽象方法会报编译错误
 */
@FunctionalInterface
public interface TestFunctionInterface {
    public void test();

    //public void test1();  //会报编译错误

    //Object中的方法不是抽象方法
    @Override
    public boolean equals(Object var1);

    //default 不是抽象方法
    public default void defaultMethod(){

    }
    //static方法不是抽象方法
    public static void staticMethod(){

    }

}

元注解

元注解就是用来标记注解的注解,也就是用来自定义注解的

1、@Retention

@Retention注解用来表示注解保留的阶段(源码、字节码、运行时)

  • @Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中,在class字节码文件中不包含
  • @Retention(RetentionPolicy.CLASS),默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
  • @Retention(RetentionPolicy.RUNTIME),注解会在class字节码文件中存在,在运行时可以通过反射获取到

自定义注解只能使用RetentionPolicy.RUNTIME

@Retention(RetentionPolicy.RUNTIME)
public @interface FirstAnnotation {

}

2、@Target

@Target用来限定注解可以使用的范围,可以是类、方法、参数等

  • @Target(ElementType.TYPE) 作用接口、类、枚举、注解
  • @Target(ElementType.FIELD) 作用属性字段、枚举的常量
  • @Target(ElementType.METHOD) 作用方法
  • @Target(ElementType.PARAMETER) 作用方法参数
  • @Target(ElementType.CONSTRUCTOR) 作用构造函数
  • @Target(ElementType.LOCAL_VARIABLE)作用局部变量
  • @Target(ElementType.ANNOTATION_TYPE)作用于注解
  • @Target(ElementType.PACKAGE) 作用于包
  • @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型
  • @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class

一般比较常用的是ElementType.TYPE

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnnotation {

}

3、@Documented

@Documented的作用是能够将注解中的元素包含到 Javadoc 中去

4、@Inherited

当一个类被@Inherited标记时,如果子类没有被其他注解标记,那么子类会继承父类的注解

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnnotation {

}

5、@Repeatable

@Repeatable标记的注解表示可以同时标记一个对象多次

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnnotation {
    RepeatAnnotionTest[] value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(FirstAnnotation.class)
public @interface RepeatAnnotionTest {
    String value() default "";
}
@RepeatAnnotionTest(value = "Java")
@RepeatAnnotionTest(value = "C++")
@RepeatAnnotionTest(value = "Python")
public class TestRepeat {

}

注解解析

上面我们介绍了元注解,也自己完成了注解的定义,但是注解只是定义出来并没有什么意义,最终我们要使用注解来帮我们完成一定的功能

这就要使用到注解解析,在Java反射中有专门用来解析注解的方法,我们可以直接使用,下面我们通过一个例子来看一下如何完成注解的解析

假如我们现在要写一个校验的注解,用来校验年龄不能低于多少岁

1、首先我们定义一个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateAge {
    int minAge() default 1;
}

2、解析注解

public class TestValidateAge {
    public static void main(String[] args){
        TestValidateAge testValidateAge = new TestValidateAge();
        testValidateAge.validateAge(15);
    }

    @ValidateAge(minAge = 20)
    public void validateAge(int age){
        System.out.println(processAnnotion(age));
    }

    //解析注解
    private String processAnnotion(int age){
        try {
           //通过反射拿到对应的方法
           Method validateAge = TestValidateAge.class.getDeclaredMethod("validateAge",int.class);
           //判断方法上是否存在指定的注解
           boolean isPresent = validateAge.isAnnotationPresent(ValidateAge.class);
           if(isPresent){
               //获取方法上的注解
               ValidateAge annotation = validateAge.getAnnotation(ValidateAge.class);
               //获取注解中属性的值
               int minAge = annotation.minAge();
               if(age < minAge ){
                   return "年龄不能低于" + minAge + "岁,校验失败";
               } else {
                   return "你的年龄是" + age + "岁,校验通过";
               }
           }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return "校验失败";
    }
}

输出结果:

年龄不能低于20岁,校验失败

通过上面的例子,我们完成了一个简单的校验注解,其实注解本身并不难,注解更像是我们对类、方法、参数等打一个标签,然后我通过反射来对我指定的标签进行相应的业务逻辑处理

我们常用的框架,如Spring、MyBatis都定义了自己的注解,原理都一样,都是定义一个注解,然后复用注解解析器来对指定的注解进行不同的业务处理

明白了这其中的原理,我们就可以自定义一些注解来完成我们的业务处理

如果感觉对你有些帮忙,请收藏好,你的关注和点赞是对我最大的鼓励!
如果想跟我一起学习,坚信技术改变世界,请关注【Java天堂】公众号,我会定期分享自己的学习成果,第一时间推送给您

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/pzjtian/article/details/107678197