如何自定义一个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都定义了自己的注解,原理都一样,都是定义一个注解,然后复用注解解析器来对指定的注解进行不同的业务处理
明白了这其中的原理,我们就可以自定义一些注解来完成我们的业务处理
本文地址:https://blog.csdn.net/pzjtian/article/details/107678197
上一篇: PHP使用栈解决约瑟夫环问题算法示例