您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
自定义java注解
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
自定义java注解
自猿其说Tech
2021-06-02
IP归属:未知
688280浏览
计算机编程
大家好,我是公众号:【java小杰要加油】,最近在项目中,发现了很多地方都用到了自定义注解, 根据自定义的注解,再去做一些个性化的操作,非常方便,今天来分享给大家 - 话不多说,直接开车 # 注解大致介绍 首先,让我们来声明一个注解 ```java // 注解可以作用在哪里 @Target({ElementType.TYPE}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYClassAnnotation { DIYEnum diyEnum(); // 年龄默认24 岁 int age() default 24; } ``` 可以注意到,我们声明的这个注解,他自己又带着很多元注解,我们依此来解释下,对应可取的值也如下 - @Target : 指此注解可以标注在哪些地方,是字段?还是类?还是方法? - TYPE :类,接口(包括注释类型)或枚举声明 - FIELD:字段声明(包括枚举常量) - METHOD:方法声明 - PARAMETER:形式参数声明 - CONSTRUCTOR:构造函数声明 - LOCAL_VARIABLE:局部变量声明 - ANNOTATION_TYPE:注释类型声明 - PACKAGE:包声明 - TYPE_PARAMETER:类型参数声明 - TYPE_USE:使用类型 - @Retention :指该注解的生命周期,存活在哪个阶段 - SOURCE:批注将被编译器丢弃 - CLASS:注释将由编译器记录在类文件中,但不必在运行时由VM保留。这是默认的行为。 - RUNTIME:注释将由编译器记录在类文件中,并在运行时由VM保存*,因此可以通过反射方式读取它们 - @Documented :指是默认情况下,带有类型的注释将由javadoc *和类似工具来记录 - @Inherited :可以继承父类注解 里面的值只能用基本类型boolean、int、double、float、long、byte、short、char和String、Enum、Class以及一些其他注解 我们一般写注解的时候。就是用图中上面那几个加粗颜色的属性和值 # 实战演练 - 其实使用这个自定义注解,千言万语就一句话 1.先声明一个自定义的注解 2.通过反射等方式取出这个注解,再根据这个注解中自己设定的值去做一些定制化的操作 - 本文将演示三种类型的自定义注解怎么用,平常开发也就这三种了(我接触的) ## 一、自定义类注解 ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Component public @interface DIYClassAnnotation { // 自定义枚举类型 DIYEnum diyEnum(); // 年龄默认24 岁 int age() default 24; } ``` 看一下这个枚举类型 ```java public enum DIYEnum { xiaoJie("小杰","打代码"), TEACHER("老师","教书"), CHEF("厨师","做饭"); private String name; private String worker; DIYEnum(String name,String worker) { this.name = name; this.worker = worker; } public String getWorker() { return worker; } public void setWorker(String worker) { this.worker = worker; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` ## 二、自定义字段注解 ```java // 注解到什么地方 属性 上 @Target({ElementType.FIELD}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYFieldAnnotation { // 性别 String sex(); } ``` ## 三、自定义方法注解 ```java // 注解到什么地方 方法 上 @Target({ElementType.METHOD}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYMethodAnnotation { // 是否校验 int verification(); // 接口名称 String interfaceName(); } ``` - 其实我们注意到,感觉大家都大差不差啊,是的没错,也就是target作用域不一样罢了 我们自定义注解定义完了,下面要开始真正使用啦 - 定义个抽象父类Person ```java // 抽象父类 public abstract class Person { public abstract void hobby(); } ``` - 定义学生Student子类 ```java @DIYClassAnnotation(diyEnum = DIYEnum.xiaoJie,age=23 ) public class Student extends Person { @DIYFieldAnnotation(sex = "男") private String sex; @Override public void hobby() { System.out.println(DIYEnum.xiaoJie.getWorker()); } } ``` - 定义老师Teacher子类 ```java @DIYClassAnnotation(diyEnum = DIYEnum.TEACHER,age=46 ) public class Teacher extends Person { @DIYFieldAnnotation(sex = "女") private String sex; @Override public void hobby() { System.out.println(DIYEnum.TEACHER.getWorker()); } } ``` - 定义厨师Chef子类 ```java @DIYClassAnnotation(diyEnum = DIYEnum.CHEF,age=50 ) public class Chef extends Person { @DIYFieldAnnotation(sex = "男") private String sex; @Override public void hobby() { System.out.println(DIYEnum.CHEF.getWorker()); } } ``` 再来一个注解工具类 ```java public class DIYAnnotationUtils { public static Person getPerson(Person ...persons){ for (Person person:persons) { // 判断这个类是否有这个注解 if (person.getClass().isAnnotationPresent(DIYClassAnnotation.class)){ // 得到这个自定义的注解 DIYClassAnnotation workerAnnotation = person.getClass().getAnnotation(DIYClassAnnotation.class); // 判断这个自定义注解注解的值是否是我们想要的 if (DIYEnum.xiaoJie.getName().equals(workerAnnotation.diyEnum().getName())){ // 反射得到这个对象的属性 Field[] fields = person.getClass().getDeclaredFields(); for (Field field:fields) { // 如果这个字段有这个注解 if (field.isAnnotationPresent(DIYFieldAnnotation.class)){ // 打印出这个属性上有这个注解的值 DIYFieldAnnotation annotation = field.getAnnotation(DIYFieldAnnotation.class); System.out.println(annotation.sex()); } } return person; } } } return null; } } ``` 最主要的就是这个工具类(用到反射),其中根据传进来的对象判断符合不符合我们的要求 (注解时的名字是不是小杰),如果符合的话,把注解在属性上的注解拿出来 - 我们通过测视类来调用一下 ```java public class Test { public static void main(String[] args) { Student student =new Student(); Chef chef = new Chef() ; Teacher teacher = new Teacher(); Person person = DIYAnnotationUtils.getPerson(student, chef, teacher); if (person != null){ person.hobby(); } } } ``` 输出结果是 ```java 男 打代码 ``` - 下面我们来演示下怎么在方法上使用这注解,最常见的组合就是自定义注解+AOP 下面来看下controller ```java @RestController public class Controller { // 此方法需要校验 @DIYMethodAnnotation(verification = 1,interfaceName = "学生爱好接口") @RequestMapping("/verification") public String verificationMethod(String id){ new Student().hobby(); return "校验"; } // 此方法不需要校验 @DIYMethodAnnotation(verification = 0,interfaceName = "老师爱好接口") @RequestMapping("/noVerification") public String noVerificationMethod(String id){ new Teacher().hobby(); return "不校验"; } // 此方法没有注解 @RequestMapping("/noAnnotation") public String noAnnotationMethod(String id){ new Chef().hobby(); return "无注解"; } } ``` 再看下切面类 本文注重讲解注解,这个切面类还有很多完善的地方不过不在本文范围内 ```java @Component @Aspect public class LogAspect { // 注解的位置 @Pointcut("@annotation(com.example.demo.annotation.DIYMethodAnnotation)") public void diyPointCut(){}; @Around("diyPointCut()") public Object diyAround(ProceedingJoinPoint joinPoint){ //获得被增强的方法相关信息 MethodSignature signature = (MethodSignature)joinPoint.getSignature(); // 获得这个方法 Method method = signature.getMethod(); // 获得这个方法上面的注解 DIYMethodAnnotation diyMethodAnnotation = method.getAnnotation(DIYMethodAnnotation.class); // 根据注解自定义的一些属性去做自定义的操作 if (diyMethodAnnotation.verification() == 1){ System.out.println("当前校验的是:"+diyMethodAnnotation.interfaceName()); System.out.println("方法名称是:"+method.getName()); System.out.println("传递参数是:"+JSON.toJSONString(joinPoint.getArgs())); } System.out.println("aop 拦截器里 verification:"+diyMethodAnnotation.verification() ); try { return joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); return null; } } } ``` 我们将项目跑起来,分别访问两个打了注解的接口 - 需要校验的接口 ```java http://localhost:8081/verification?id=1 当前校验的是:学生爱好接口 方法名称是:verificationMethod 传递参数是:["1"] aop 拦截器里 verification:1 打代码 ``` - 不需要校验的接口 ```java http://localhost:8081/noVerification?id=1 aop 拦截器里 verification:0 做饭 ``` - 没有注解的接口 ```java http://localhost:8081/noAnnotation?id=1 做饭 ``` 由输出结果可以得出一个结论, - 没有注解的接口,走不到AOP,因为我们AOP配置的是只有注解的接口才进行AOP校验, - 如果接口上有注解的话,又有两种情况(这是我们自己设置的) - verification 0 的时候 这个注解也不进行特别的操作 输出 ”当前校验的是,方法名称是,传递参数是 - verification 1 的时候 这个注解进行特别的操作 综上所述,我们在日常开发中,如果对某个类/字段/方法有什么特殊的要求的话,可以使用自定义注解,再通过反射获取到此注解,再根据这个注解中自定义的值在进行我们自定义的操作 ![](//img1.jcloudcs.com/developer.jdcloud.com/26e85f60-96df-4017-9046-47dfc711640620210602144157.png) ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:中台技术部-基础平台组 邢焕杰 ------------
原创文章,需联系作者,授权转载
上一篇:nginx反向代理时保持长连接
下一篇:最高评级!京东行云DevOps平台获得信通院先进级平台认证
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号