当前位置: 首页 > news >正文

java注解(实现原理及自定义注解)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

    • 一、注解的实现原理
      • 1.1 怎样定义注解(打标签)
      • 1.2 注解处理器 (决定了这个标签到底能干嘛)
      • 1.3 常用标签的处理过程
    • 二、怎样创建自定义注解
    • 总结


本文不是讲某个注解什么含义,而是讲注解的实现原理,以及自定义注解的创建。

我们最开始会接触一些框架的注解,例如spring等,但是这些注解是怎么起作用的呢?我们怎样实现自己的注解呢?

一、注解的实现原理

一言以蔽之,原理就是打个标签(类或方法等上使用 @注解),然后在特定时机处理标签(检测到标签后,加相应的功能)。

我们使用注解时,只接触打标签,也就是编码加上各种 @注解,而标签的处理则隐藏在框架或工具类中,嫌少接触。

1.1 怎样定义注解(打标签)

如下,跟定义接口一样,不过类型是@interface。

@Target(ElementType.METHOD)  //  注解用于方法上
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
public @interface MyAnnotation {String description();int length() default 0;
}

这个注解上面的注解,是 java定义的元注解,用于描述注解,常用的有:

  • Target:描述了注解修饰的对象范围。(TYPE-描述类、接口或enum,METHOD-描述方法等)
  • Retention: 表示注解保留时间长短。(SOURE-在源文件中有效,编译过程中忽略,RUNTIME-在运行时有效)

其中,这个注解可以有属性,也可以没有属性,属性可以有默认值,也可以没有默认值。这些不重要,只是标签的附属功能。

当在类等上面使用 @ 加上注解后,只是给它打了个标签,并没有任何功能,这个标签到底对应什么功能,取决于它的注解处理器

1.2 注解处理器 (决定了这个标签到底能干嘛)

注解会被编译进class文件中,所以获取到class对象后,就能获取到其定义在类、方法、属性等上的标签了。

所以怎样获取注解?

class对象--反射获取到方法、属性等--获取方法或属性上的注解。
Class c = XX.class;// 获取所有字段for(Method m : c.getMethods()){// 判断这个字段是否有MyAnnotion注解if(f.isAnnotationPresent(MyAnnotation.class)){MyAnnotion annotation = f.getAnnotation(MyAnnotation.class);}}

1.3 常用标签的处理过程

例如经典的@Bean标签(一般不再单独使用,仅用于理解),表示这是一个spring的Bean,会初始化到spring容器中。
那这个标签是怎样起作用的呢?

首先,spring定义好了Bean注解。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {@AliasFor("name")String[] value() default {};@AliasFor("value")String[] name() default {};// 其他属性忽略
}

你在某个类型上也使用了该注解,在spring容器初始化时(refresh入口的那一堆难以理解的源码),会检测这个类有没有Bean注解,有的话,就注册。

只看其中判断有没有注解的代码。

// 判断该类是否有某注解
protected Boolean hasAnnotation(String typeName) {try {// 获取class对象Class<?> clazz = ClassUtils.forName(typeName, this.getClass().getClassLoader());// 关键是clazz.getAnnotation,获取类上的注解return (this.considerMetaAnnotations ? AnnotationUtils.getAnnotation(clazz, this.annotationType) : clazz.getAnnotation(this.annotationType)) != null;} catch (Throwable var3) {// ...}
}

二、怎样创建自定义注解

某些场景使用自定义注解,能大大简化代码。例如调用某controller方法时,添加认证信息;两个对象之间属性拷贝,处理不同名属性等。
下面以对象拷贝为例,例如对象A,对象B之间的拷贝,期望自动处理不同名属性时。

// 原实体
@Data
public class Source {// 按属性名称转@PropertyTransform(name = "targetName",transformStrategy = TransformStrategy.DEFAULT)String sourceName;
}// 目标实体
@Data
public class Target {String targetName;
}// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Component
public @interface PropertyTransform {/*** 目标属性* @return*/@AliasFor("name")String value() default "";/*** 目标属性* @return*/@AliasFor("value")String name() default "";
}// 注解处理器
public class PropertiesTransformUtils {@SneakyThrowspublic static Object copy(Object source,Class<?> target){Class<?> sourceClass = source.getClass();//获取原实体下的所有属性Field[] fields = sourceClass.getDeclaredFields();// 初始化目标实体Object targetObj = target.newInstance();//记录转换名称后的属性名以及属性值Map<String,Object> objMap = new HashMap<>(fields.length);for (Field field : fields) {field.setAccessible(true);//获取属性上的注解PropertyTransform propertyTransform = field.getAnnotation(PropertyTransform.class);if (propertyTransform != null){objMap.put(propertyTransform.name(),field.get(source));}//将值拷贝到目标实体Object targetObj = target.newInstance();for (Field declaredField : target.getDeclaredFields()) {Object value = objMap.getOrDefault(declaredField.getName(),null);declaredField.setAccessible(true);declaredField.set(targetObj,value);}return targetObj;}

总结

注解的实现分为三步:定义注解;在代码中使用注解;注解处理器赋予相应的功能。不管是框架内的注解,还是自定义注解,都是按照这三步来的。不太好理解的可能就是注解处理器赋予的各种功能。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Flask获取请求信息
  • Stable Diffusion绘画 | ControlNet应用-NormalMap(法线贴图)
  • WPF APP生命周期和全局异常捕获
  • 使用Qdrant+FastText实现向量存储和检索
  • YOLO基础-目标检测的性能指标详解与计算方法
  • vulnhub系列:devguru
  • [SWPUCTF 2021 新生赛]PseudoProtocols(构造伪协议)
  • C# 计算两两坐标之间的距离(SIMD加速)
  • 常用的数据结构有哪些?
  • Docker 部署RocketMQ
  • 若依框架将Mybatis改成MybatisPlus
  • RabbitMQ面试题汇总
  • 【数学建模】介绍论文书写格式
  • YOLOv10实时端到端目标检测
  • 智慧楼宇公厕系统小程序,提高卫生间管理使用效率
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CODING 缺陷管理功能正式开始公测
  • HomeBrew常规使用教程
  • Java 多线程编程之:notify 和 wait 用法
  • Java读取Properties文件的六种方法
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • MySQL QA
  • PermissionScope Swift4 兼容问题
  • QQ浏览器x5内核的兼容性问题
  • vue-cli3搭建项目
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 分布式熔断降级平台aegis
  • 欢迎参加第二届中国游戏开发者大会
  • 实习面试笔记
  • 物联网链路协议
  • 详解NodeJs流之一
  • 怎样选择前端框架
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • ​MySQL主从复制一致性检测
  • ​决定德拉瓦州地区版图的关键历史事件
  • ###C语言程序设计-----C语言学习(6)#
  • #1014 : Trie树
  • (1)(1.11) SiK Radio v2(一)
  • (1)常见O(n^2)排序算法解析
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (算法)求1到1亿间的质数或素数
  • (一)Neo4j下载安装以及初次使用
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)【Hibernate总结系列】使用举例
  • (转载)Linux网络编程入门
  • /var/spool/postfix/maildrop 下有大量文件
  • @cacheable 是否缓存成功_Spring Cache缓存注解
  • [AIGC] Kong:一个强大的 API 网关和服务平台
  • [Angular] 笔记 6:ngStyle
  • [Assignment] C++1
  • [AutoSAR系列] 1.3 AutoSar 架构
  • [BT]BUUCTF刷题第4天(3.22)