用自定义注解做点什么——自定义注解有什么用

用自定义注解做点什么

前言


你不一定听过注解,但你一定对@Override不陌生。

当我们重写父类方法的时候我们就看到了@Override。我们知道它表示父类方法被子类重写了。

现在告诉你,@Override就是一个注解。

也许你会疑惑注解是什么?
注解(annotation)是JDK5之后引进的新特性,是一种特殊的注释,之所以说它特殊是因为不同于普通注释(comment)能存在于源码,而且还能存在编译期跟运行期,会最终编译成一个.class文件,所以注解能有比普通注释更多的功能。

接下来,先入个门,然后通过实战来证明注解有多“猛”。

PS : 如果已经了解的小伙伴可自行跳到 自定义注解实战。

自定义注解入门


我们对于注解的认识大多数来源于标准注解(也称为内建注解)。

标准注解表示的意义
@Override用于标识该方法继承自超类 当父类的方法被删除或修改了,编译器会提示错误信息
@Deprecated表示该类或者该方法已经不推荐使用 如果用户还是要使用,会生成编译的警告
@SuppressWarnings用于忽略的编译器警告信息

Java不仅仅提供我们原有的注解使用,它还允许我们自定义注解。比如你可以像这样:

public @interface DoSomething {public String name() default "write";
}

这是最简单的注解声明。
尽管看上去像是接口的写法,但完全不是一回事。这一点要注意。
而使用注解也很简单,可以像这样:

@DoSomething(name = "walidake")//可以显式传值进来,此时name=walidake
public class UseAnnotation {}@DoSomething//如果不传值,则默认name=我们定义的默认值,即我们上面定义的"write"
public class UseAnnotation {}

需要注意的是当注解有value()方法时,不需要指明具体名称

public @interface DoSomething {public String value();public String name() default "write";
}@DoSomething("walidake")
public class UseAnnotation {}

然而“最简单的自定义注解”并没有特别的意义。所以,这时候我们需要引入一个元注解的概念。

我们需要知道这些概念:
“普通注解”只能用来注解“代码”,而**“元注解”只能用来注解 “普通注解”**。
自定义注解是“普通注解”

JDK5时支持的元注解有@Documented @Retention @Target @Inherited,接下来分别介绍它们修饰注解的效果。

@Documented
@interface DocumentedAnnotation{}@interface UnDocumentedAnnotation{}@DocumentedAnnotation
@UnDocumentedAnnotation
public class UseDocumentedAnnotation{}

打开小黑窗,运行javadoc UseDocumentedAnnotation.java

运行结果:

img

结论:可以看到,被@Documented修饰的注解会生成到javadoc中,如@DocumentedAnnotation。
而不被@Documented修饰的注解(@UnDocumentedAnnotation)不会生成到javadoc中。

注解的级别
@Retention可以设置注解的级别,分为三种,都有其特定的功能。
这个元注解是我们关注的重点,后面实战我们会用到。

注解级别存在范围主要用途
SOURCE 源码级别注解只存在源码中功能是与编译器交互,用于代码检测。 如@Override,@SuppressWarings。 额外效率损耗发生在编译时
CLASS 字节码级别注解存在源码与字节码文件中主要用于编译时生成额外的文件,如XML,Java文件等,但运行时无法获得。 这个级别需要添加JVM加载时候的代理(javaagent),使用代理来动态修改字节码文件
RUNTIME 运行时级别注解存在源码,字节码与Java虚拟机中主要用于运行时反射获取相关信息

限制注解使用的范围
注解默认可以修饰各种元素,而使用@Target可以限制注解的使用范围。

例如,可以限定注解只能修饰方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}

上面的代码将注解的使用范围限制在了方法上,而不能用来修饰类。

试着用@Override修饰类会得到“The annotation @Override is disallowed for this location”的错误。

@Target支持的范围(参见ElementType):

1) 类,接口,注解;
2) 属性域;
3) 方法;
4) 参数;
5) 构造函数;
6) 局部变量;
7) 注解类型;
8) 包

注解的继承
@Inherited可以让注解类似被“继承”一样。
通过使用@Inherited,可以让子类对象使用getAnnotations()获取父类@Inherited修饰的注解。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Inheritable{}@interface UnInheritable{}public class UseInheritedAnnotation{@UnInheritable@Inheritablepublic static class Super{}public static class Sub extends  Super {}public static void main(String... args){Super instance=new Sub();//result : [@com.walidake.annotation.util.Inheritable()]System.out.println(Arrays.toString(instance.getClass().getAnnotations()));}
}

我们干脆用@Documented查看类结构。发现:

img

img

这是不是恰恰证明了这种是伪继承的说法,而不是真正的继承。

自定义注解实战


引言
Java Web开发中,对框架的理解和掌握是必须的。而在使用大多数框架的过程中,一般有两种方式的配置,一种是基于xml的配置方式,一种是基于注解的方式。然而,越来越多的程序员(我)在开发过程中享受到注解带来的简便,并义无反顾地投身其中。

ORM框架,像Hibernate,Mybatis就提供了基于注解的配置方式。我们接下来就使用自定义注解实现袖珍版的Mybatis,袖珍版的Hibernate。

这很重要
说明:实战的代码会被文章末尾附上。而实际上在之前做袖珍版框架的时候并没有想到会拿来做自定义注解的Demo。因此给出的代码涉及了其他的一些技术,例如数据库连接池,动态代理等等,比较杂。
在这个篇幅我们只讨论关于自定义注解的问题,至于其他的技术后面会开多几篇博文阐述。(当然这么多前辈面前不敢造次,有个讨论学习的氛围是很好的~)

那么在自定义注解框架前,我们需要花点时间浏览以下几个和Annotation相关的方法。

方法名用法
Annotation getAnnotation(Class annotationType)获取注解在其上的annotationType
Annotation[] getAnnotations()获取所有注解
isAnnotationPresent(Class annotationType)判断当前元素是否被annotationType注解
Annotation[] getDeclareAnnotations()与getAnnotations() 类似,但是不包括父类中被Inherited修饰的注解

Mybatis 自定义注解

本节目标:自定义注解实现Mybatis插入数据操作。
本节要求:细心观察使用自定义注解的步骤。

Step 1 : 声明自定义注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Insert {public String value();
}

Step 2 : 在规定的注解使用范围内使用我们的注解

public interface UserMapper {@Insert("insert into user (name,password) values (?,?)")public void addUser(String name,String password);}

Step 3 : 通过method.getAnnotation(Insert.class).value()使用反射解析自定义注解,得到其中的sql语句

//检查是否被@Insert注解修饰
if (method.isAnnotationPresent(Insert.class)) {//检查sql语句是否合法//method.getAnnotation(Insert.class).value()取得@Insert注解value中的Sql语句sql = checkSql(method.getAnnotation(Insert.class).value(),Insert.class.getSimpleName());//具体的插入数据库操作insert(sql, parameters);
}

Step 4 : 根据实际场景调用Step 3的方法

UserMapper mapper = MethodProxyFactory.getBean(UserMapper.class);
mapper.addUser("walidake","665908");

运行结果:

img

以上节选自annotation中Mybatis部分。具体CRUD操作请看源码。

总结一下从上面学到的东西:
1.声明自定义注解,并限制适用范围(因为默认是通用)
2.规定范围内使用注解
3.isAnnotationPresent(Insert.class)检查注解,getAnnotation(Insert.class).value()取得注解内容
4.根据实际场景应用

Hibernate 自定义注解

本节目标:自定义注解使实体自动建表(即生成建表SQL语句)
本节要求:动手操作,把未给全的代码补齐。
本节规划:仿照Hibernate,我们大概会需要@Table,@Column,还有id,我们这里暂且声明为@PrimaryKey

仿照自定义Mybatis注解的步骤:

/*** 可根据需要自行定制功能*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {String name() default "";}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {// 列名 默认为""String name() default "";// 长度 默认为255int length() default 255;// 是否为varchar 默认为trueboolean varchar() default true;// 是否为空 默认可为空boolean isNull() default true;
}/*** 有需要可以拆分成更小粒度* @author walidake**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PrimaryKey {String name() default "";
}

完成Step 1,接下来是Step 2。

@Table
public class Person {@PrimaryKeyprivate int id;@Column(isNull = false, length = 20)private String username;...
}

Step 3,新建一个叫做SqlUtil的类,使用Class(实体类).isAnnotationPresent(Table.class)取到@Table注解的内容。

而我们如何取到@Column和@PrimaryKey的内容?
使用反射,我们可以很容易做到。

// 反射取得所有Field
Field[] fields = clazz.getDeclaredFields();
...
...
// 获取注解对象
column = fields[i].getAnnotation(Column.class);
// 设置访问私有变量
fields[i].setAccessible(true);
// 取得@Column的内容
columnName = "".equals(column.name()) ? fields[i].getName(): column.name();

反射的内容后面再写。(感觉每一篇都给自己挖了很多坑后面去填)

Step 4套入使用场景

String createSql = SqlUtil.createTable(clazz);
...
connection.createStatement().execute(createSql);

运行结果:

img

运行结果正确!

自此我们完成了实战模块的内容。当然关于Hibernate的CRUD也可以用同样的方法做到,更进一步还可以把二级缓存整合进来,实现自己的一个微型框架。尽管现有的框架已经很成熟了,但自己实现一遍还是能收获很多东西。

可以看出来,注解简化了我们的配置。每次使用注解只需要@注解名就可以了,就跟吃春药一样“爽”。不过由于使用了反射,后劲太“猛”,jvm无法对代码优化,影响了性能。这一点最后也会提及。

另外提一点,之前想格式化hibernate生成的SQL,做大量搜索后被告知“Hibernate 使用的是开源的语法解析工具 Antlr,需要进行 SQL 语法解析,将 SQL 语句整理成语法树”。也算一个坑吧~
不过后来找到一个除了建表SQL以外的格式化工具类,觉得还不错就也分享了。可以在源码中找到。

最后说点什么
可以发现我们使用运行时注解来搭建我们的袖珍版ORM框架,因为运行时注解来搭建框架相对容易而且适用性也比较广,搭建的框架使用起来也比较简单。但在此基础上因为需要用到反射,其效率性能相对不高。因此,多数Web应用使用运行时注解,而像Android等对效率性能要求较高的平台一般使用源码级别注解来搭建。下一节我们讨论怎么玩一玩源码级注解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++ 查找文件夹下最新创建的文件_云计算开发总结:搜索Linux文件和文件夹的方法...

当下,随着Linux在物联网、云技术、超级计算和人工智能等领域扮演关键角色,各种会议和新版本的发布令人应接不暇,Linux将迎来一个激动人心的“云时代”。如果你想把握这个风口,现在是学习Linux技术的最佳时期。今天千锋广州云计算培…

RSA 非对称加密原理

RSA 加密原理 步骤说明描述备注1找出质数P 、Q-2计算公共模数N P * Q-3欧拉函数φ(N) (P-1)(Q-1)-4计算公钥E1 < E < φ(N)E的取值必须是整数 E 和 φ(N) 必须是互质数5计算私钥DE * D % φ(N) 1-6加密C &#xff1d; M E mod NC&#xff1a;密文 M&#xff1a;明文7…

浅谈对称加密与非对称加密

在数字加密算法中&#xff0c;通过可划分为对称加密和非对称加密。 一&#xff1a;什么是对称加密&#xff1f; 在对称加密算法中&#xff0c;加密和解密使用的是同一把钥匙&#xff0c;即&#xff1a;使用相同的密匙对同一密码进行加密和解密&#xff1b; 加密过程如下&…

ios跨线程通知_一种基于Metal、Vulkan多线程渲染能力的渲染架构

快手Y-tech 原创最新技术干货分享随着3D渲染场景规模越来越复杂&#xff0c;单线程渲染架构在满足业务性能要求时已经捉襟见肘&#xff0c;因此&#xff0c;多线程渲染显得愈发重要。本文首先介绍了新一代图形渲染接口Metal、Vulkan&#xff0c;以及它们的多线程渲染特性&…

58同城面试盘点

58同城面试盘点 1.一张订单表&#xff0c;有user_name,order_id,order_time,order_amount 四个字段&#xff0c;怎么取出每个用户2021年10月以来第一个订单的金额&#xff08;下单时间格式为’yyyy-MM-dd HH:mm:ss’&#xff09;&#xff1f; select user_name,order_id,orde…

stringbuffer判断是否为空

StringBuffer sbnew StringBuffer();if(sb!null && sb.length()>0){System.out.println("证明sb不为空!"); }

virtualbox: win11主机安装deepin双向复制问题

virtualbox: win11主机安装deepin双向复制问题1.安装virtualbox增强组件(确保光驱可用)2.终端挂载3. 运行BoxLinuxAdditions4. 重启虚拟机&#xff0c;验证OK&#xff01;使用virtualbox安装深度系统deepin虚拟&#xff0c;发现虚拟机和宿主机之间不能双向复制&#xff0c;已经…

基坑监测日报模板_刚刚!温州瓯海突发塌陷,初步判断为临近地块地下室基坑支护桩移位...

资料来源&#xff1a;瓯海新闻网 | 温州百事通 | 土木吧 | 岩土新鲜事 等版权归原作者所有如有侵权请联系删除9月10日中午11点左右&#xff0c;温州市瓯海区娄桥街道商汇路道路塌陷。塌陷路面位于商汇路的公交车站旁&#xff0c;几十米长的路面已经开裂&#xff0c;公交站台发生…

java 从一个总的list集合中,去掉指定的集合元素,得到新的集合——removeAll()

/*** 两个list集合的差集* author*/ public class ListSubstract {public static void main(String[] args) {List<String> list new ArrayList<>();//作为总的listList<String> existList new ArrayList<>();//存在的listlist.add("aa");…

virtualbox:win11上的deepin如何设置与宿主机共享文件

1. 安装virtualbox增强功能 这个没有测试&#xff0c;只是理论上需要。我在上一篇帖子《virtualbox: win11主机安装deepin双向复制问题》已经安装了增强功能&#xff0c;大家可以参考安装。 2.在virtualbox上配置共享 2.1 关闭虚拟机&#xff0c;进行设置 共享文件夹路径点击…

三角形外接球万能公式_秒杀三角形问题!!三角形分角线的几个重要结论及其应用...

点击“高中数学资料共享”关注我们解三角形问题在高考中的选择、填空、解答题一般都会涉及到(最少也有两块涉及到)&#xff0c;其中有一类涉及角平分线长度、中线长、高线长度问题&#xff0c;难度不大&#xff0c;但运算量不小&#xff0c;那我们如果在考试中能在最短时间内把…

java自定义注解为空值——自定义注解的魅力你到底懂不懂

前言 你知道自定义注解的魅力所在吗&#xff1f; 你知道自定义注解该怎么使用吗&#xff1f; 本文一开始的这两个问题&#xff0c;需要您仔细思考下&#xff0c;然后结合这两个问题来阅读下面的内容&#xff1b; 本文主线&#xff1a; 注解是什么&#xff1b;实现一个自定义注…

uniapp动态修改样式_掌握Photoshop图层样式技术

凹凸贴图效果“等等&#xff0c;什么&#xff1f;” 您会惊叹&#xff1a;““图层样式”菜单中没有凹凸贴图效果&#xff01;” 的确如此&#xff0c;但是通过将“图案覆盖”和“斜面和浮雕”结合使用&#xff0c;我们可以使用可控光源实现带纹理的凹凸贴图表面。此技术需要两…

一个专业搬砖人的幻想:全国实现旬休制度

每逢过年&#xff0c;总觉得假期不够忙&#xff0c;如果折腾折腾&#xff0c;应该还是可以的。 于是想了一个替代方案&#xff0c;以弥补春节余额不足、各种假期调休的诟病。以下是设计与比较表单&#xff1a; 调整后可以实现以下愿望&#xff1a; 旬休是每月分上、中、下三…

IoT -- (七)MQTT协议详解

MQTT是什么&#xff1f; MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;Publish/Subscribe&#xff09;模式的轻量级通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff…

刀片服务器改台式电脑_服务器到底是个什么东东?跟电脑有啥区别?电脑知识学习!...

一位朋友留言点的内容&#xff0c;想了解服务器方面的知识&#xff0c;对于普通用户而言&#xff0c;确实对服务器感觉很神秘&#xff0c;不知道服务器到底是个什么东东&#xff0c;我保证看完这篇&#xff0c;你就会明白服务器到底是个啥了。首先可以很明确的告诉你&#xff0…

IoT -- (一) 物联网平台架构设计分析

现在网上讨论的有关物联网的帖子非常之多&#xff0c;但大部分都是介绍理论或者有关硬件&#xff0c;通讯相关的问题&#xff0c;比如物联网模块&#xff0c;物联网通讯协议MQTT、XMPP、NB_IOT等&#xff0c;个人认为这些只是物联网中一部分&#xff0c;而涉及到物联网的设备如…

SFTP批量下载与中文文件名乱码问题

一、批量下载 #!/bin/bashUSERroot #密码 PASSWORD123456 #下载文件目录 SRCDIR/data #FTP目录(待下载文件目录) DESDIR/ydfile #银联IP IP 192.111.111.111 #端口 PORT22# 清空当前目录下的旧文件 rm -rf /data/*#连接远程服务器摘取数据资源 lftp sftp://${USER}:${PASSWOR…

华为路由器hilink怎么用_华为无线充电怎么用?MatePadPro无线充电使用方法

越来越多的华为产品支持无线充电&#xff0c;比如Mate 30 Pro支持最高27W无线超级快充&#xff0c;FreeBuds 3 蓝牙无线耳机、荣耀V30 Pro&#xff0c;MatePad Pro平板也都支持无线充电。今天就跟大家分享华为手机无线充电技术原理图&#xff0c;无线充怎么用&#xff0c;无线充…

python乘法表运算_Python入门教程(三):史上最全的Numpy计算函数总结,建议收藏!...

点击上方 蓝字 关注我们Numpy提供了灵活的、静态类型的、可编译的程序接口口来优化数组的计算&#xff0c;也被称作向量操作&#xff0c;因此在Python数据科学界Numpy显得尤为重要。Numpy的向量操作是通过通用函数实现的。今天小编会给大家较为全面地介绍下Numpy的通用函数。01…