注解的定义与处理
2022/7/12 23:31:05
本文主要是介绍注解的定义与处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
定义注解
使用 @Interface
定义注解
public @interface Report { int type() default 0; String level() default "info"; String value() default ""; }
注解的参数类似无参方法,可以使用default
设置一个默认值,最常用的参数建议命名为value
元注解
可以修饰其他注解的注解称为元注解,对于元注解以使用为主,通常不去编写
@Target
@Target
可以定义注解可以用于源码哪些位置
- 类或接口:ElementType.TYPE
- 字段:ElementType.FIELD
- 方法:ElementType.METHOD
- 构造方法:ElementType.CONSTRUCTOR
- 方法参数:ElementType.PARAMETER
例如想要让自定义的注解可作用于方法上,可以通过这样的方式定义:
@Target({ElementType.METHOD}) public @interface Report { int type() default 0; String level() default "info"; String value() default ""; }
@Retention
该元注解主要定义了注解的生命周期
- 仅编译期:RetentionPolicy.SOURCE
- 仅 class 文件:RetentionPolicy.CLASS
- 运行期:RetentionPolicy.RUNTIME
像
@Override
就是属于第一种,因为其主要作用是让编译器检查该方法是否重写,并不会进入 class 文件
如果@Retention
不存在,则默认为 CLASS 。一般定义的注解都是 RUNTIME 的,所以需要加上@Retention(RetentionPolicy.RUNTIME)
@Repeatable
这个元注解主要是定义注解是否可以重复,用的不多
处理注解
注解本身对代码逻辑没有影响,如何使用注解由工具决定。SOURCE 类型的注解主要由编译器使用,因此我们一般只使用,不编写。CLASS 类型的注解主要由底层工具库使用,涉及到 class 的加载,一般我们很少用到。只有 RUNTIME 类型的注解不但要使用,还经常需要编写
因为注解定义后也是一种 class ,所有注解都继承自java.lang.annotation.Annotation
,所以读取注解需要使用到反射
一个简单的例子
注释类
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; int type() default 0; }
实体类
@MyAnnotation(value = "Chinese",type = 1) public class Person { private String name; private int age; }
测试类
@SpringBootTest public class AnnotationTest { @Test public void testMethod() { // 方式1 // Class<Person> personClass = Person.class; // if (personClass.isAnnotationPresent(MyAnnotation.class)) { // MyAnnotation annotation = personClass.getAnnotation(MyAnnotation.class); // String value = annotation.value(); // int type = annotation.type(); // System.out.println(value + "---" + type); // } //---------------------------------------------- // 方式2 MyAnnotation annotation = Person.class.getAnnotation(MyAnnotation.class); if(annotation != null){ System.out.println(annotation.value()); System.out.println(annotation.type()); } } }
在上面的例子中,注解是作用在类上的,作用在方法、字段和构造方法的注解处理和它大同小异,但是作用在方法参数上的注解处理起来会比较麻烦一些:因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示
读取方法参数的注解
方法参数注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface MySpeak { String value() default ""; String toWho() default "nobody"; boolean required() default true; }
实体类
@MyAnnotation(value = "Chinese", type = 1) public class Person { private String name; private int age; public void speak( @MySpeak(value = "hello", toWho = "tom", required = true) @NotNull String msg ) { System.out.println(msg); } }
测试类
@SpringBootTest public class AnnotationTest { @Test public void testFunction() throws Exception{ Class<Person> personClass = Person.class; // 获取方法 Method speakMethod = personClass.getMethod("speak", String.class); // 获取所有参数的注解 Annotation[][] anno = speakMethod.getParameterAnnotations(); // anno[0]代表第一个参数的注解,anno[0][0]代表第一个参数的第一个注解 for(Annotation a : anno[0]){ if(a instanceof MySpeak){ MySpeak mp = (MySpeak) a; }else if(a instanceof NotNull){ NotNull nn = (NotNull) a; } } } }
使用注解
注释类
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Range { int min() default 0; int max() default 255; }
实体类
@MyAnnotation(value = "Chinese", type = 1) @Data @AllArgsConstructor public class Person { @Range(min = 3,max = 16) private String name; @Range(max = 24) private String city; private int age; public void speak( @MySpeak(value = "hello", toWho = "tom", required = true) @NotNull String msg ) { System.out.println(msg); } }
测试类
@SpringBootTest public class AnnotationTest { @Test public void testFunction() throws Exception { Person person = new Person("tom", "hebeishengshijiazhuangshixianhuaqu", 18 ); check(person); } //注释逻辑 public void check(Person person) throws Exception { //获取所有字段 Field[] fields = Person.class.getDeclaredFields(); for(Field f : fields){ Range range = f.getAnnotation(Range.class); //判断字段上是否使用注解 @range if(range != null){ //因为是私有属性所以必须设置为true f.setAccessible(true); //获取每个字段的值 Object val = f.get(person); if(val instanceof String){ String s = (String) val; //判断长度 if(s.length() < range.min() || s.length() > range.max()){ throw new RuntimeException("数据长度不合法"); }else{ System.out.println("数据长度合法"); } } } } } }
输出
数据长度合法 java.lang.RuntimeException: 数据长度不合法
通过这个例子可以明显的观察到,定义了注解对实际的业务逻辑完全没有影响,还是需要靠编写代码来完成注解的逻辑。比如,定义了@Range
注解,系统不会为我们自动生成检测字段长度是否合法的逻辑,而是需要自己通过反射机制来完成注解的逻辑
参考内容
注解
这篇关于注解的定义与处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行
- 2024-05-08阿里云域名注册流程,分享给第一次购买域名的新手站长!