注解,和反射一样,是Java中最重要却最容易被人遗忘的知识点。哪怕Spring、SpringMVC、SpringBoot等框架中充满了注解,我们还是选择性地忽视它。很多人不明白它是怎么起作用的,甚至有人把它和注释混淆...工作中也只是机械性地在Controller上加@RequestMapping。是的,我们太习以为常了,以至于觉得它应该就是如此。

内容介绍:

  • 两件小事
  • 注解的作用
  • 注解的本质
  • 反射注解信息
  • 元注解
  • 属性的数据类型及特别的属性:value和数组

两件小事

去年工作中,我遇到的两件事让注解重新走进我的视野。

第一,18年6月我去了北京,参与开发了某中国五百强企业的一个加密系统,第一次接触到了SpringBoot。当我发现一个demo项目只要简单地写个启动类并加上 @SpringBootApplication就可以直接访问Controller时,感到非常震撼。整个demo没有一个配置文件,连web.xml也没有。

由于开发进度很赶,当时没时间去研究它是如何做到的,但这件事让我意识到自己对注解还是了解得太少。

第二,来到杭州后我又参与开发了一个金融借贷系统,那阵子对接了很多第三方的风控接口:

对签名验签不了解的朋友,可以百度一下。总之,每对接一个接口,都要在开头进行数据校验。一两个接口也就算了,但每次对接风控,基本上都要写10+多个方法。每个方法开头都写一份签名验签的代码,显然太冗余了。我当时的做法是将验签代码抽取成方法,方便复用,自以为算是一种改良了,直到我看到同事用了切面...40米的大刀拦腰砍去,给每个方法都做了签名验签:

注意,实际上切面的作用是在方法前后,而不是方法内部的前后。上面这样画,仅仅为了更形象

这两件事,让我知道,是时候重新学习一下注解了。


注解的作用

格式

public @interface 注解名称{属性列表;
}

格式有点奇怪,我们稍后再研究。

分类

大致分为三类:自定义注解、JDK内置注解、还有第三方框架提供的注解。

自定义注解就是我们自己写的注解。JDK内置注解,比如@Override检验方法重写,@Deprecated标识方法过期等。第三方框架定义的注解比如SpringMVC的@Controller等。

使用位置

实际开发中,注解常常出现在类、方法、成员变量、形参位置。当然还有其他位置,这里不提及。

作用

如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据。比如程序只要读到加了@Test的方法,就知道该方法是待测试方法,又比如@Before注解,程序看到这个注解,就知道该方法要放在@Test方法之前执行。

级别

注解和类、接口、枚举是同一级别的。


注解的本质

@interface和interface这么相似,我猜注解的本质是一个接口。

为了验证这个猜测,我们做个实验。先按上面的格式写一个注解

属性先不写

编译后得到字节码文件

通过XJad工具反编译MyAnnotation.class

我们发现,@interface变成了interface,而且自动继承了Annotation

既然确实是个接口,那么我们自然可以在里面写方法

得到class文件后反编译

由于接口默认方法的修饰符就是public abstract,所以可以省略,直接写成:

虽说注解的本质是接口,但是仍然有很多怪异的地方,比如使用注解时,我们竟然可以给getValue赋值:

你见过给方法赋值的操作吗?(别闹了,你脑中想到的是给方法传参)。虽然这里的getValue可能不是指getValue(),底层或许是getValue()返回的一个同名变量。但不管怎么说,还是太怪异了。所以在注解里,类似于String getValue()这种,被称为“属性”。给属性赋值显然听起来好接受多了。

另外,我们还可以为属性指定默认值:

当没有赋值时,属性将使用默认值,比如上面的defaultMethod(),它的getValue就是“no description"。

基于以上差异,以后还是把注解单独归为一类,不要当成接口使用。


反射注解信息

上文已经说过,注解就像一个标签,是贴在程序代码上供另一个程序读取的。所以三者关系是:

要牢记,只要用到注解,必然有三角关系:定义注解,使用注解,读取注解。仅仅完成前两步,是没什么卵用的。就好比你写了一本武林秘籍却没人去学,那么这门武功还不如一把菜刀。

所以,接下来我们写一个程序读取注解。读取注解的思路是:

反射获取注解信息:

我们发现,Class、Method、Field对象都有个getAnnotation(),可以获取各自位置的注解信息。

但是控制台提示“空指针异常”,IDEA提示我们:Annotation 'MyAnnotation.class' is not retained for reflective。直译的话就是:注解MyAnnotation并没有为反射保留。

这是因为注解其实有所谓“保留策略”的说法。大家学习JSP时,应该学过<!-- -->和<%-- -->的区别:前者可以在浏览器检查网页源代码时看到,而另一个在服务器端输出时就被抹去了。同样的,注解通过保留策略,控制自己可以保留到哪个阶段。保留策略也是通过注解实现,它属于元注解,也叫元数据。


元注解

所谓元注解,就是加在注解上的注解。作为普通程序员,常用的就是:

  • @Documented

用于制作文档,不是很重要,忽略便是

  • @Target

加在注解上,限定该注解的使用位置。不写的话,好像默认各个位置都是可以的。如果需要限定注解的使用位置,可以在自定义的注解上使用该注解。我们本次默认即可,不特别限定。

  • @Retention(注解的保留策略)

注解的保留策略有三种:SOURCE/ClASS/RUNTIME

  • 注解主要被反射读取
  • 反射只能读取内存中的字节码信息
  • RetentionPolicy.CLASS指的是保留到字节码文件,它在磁盘内,而不是内存中。虚拟机将字节码文件加载进内存后注解会消失
  • 要想被反射读取,保留策略只能用RUNTIME,即运行时仍可读取

重新运行程序,成功读取注解信息:

注意,defaultMethod()反射得到的注解信息是:no description。就是MyAnnotion中getValue的默认值。

但是,注解的读取并不只有反射一种途径。比如@Override,它由编译器读取(你写完代码ctrl+s时就编译了),而编译器只是检查语法错误,此时程序尚未运行。所以,我猜@Override的保留策略肯定不是RUNTIME:

保留策略为SOURCE,仅仅是源码阶段,编译成.class文件后就消失

属性的数据类型及特别的属性:value和数组

属性的数据类型

  • 八种基本数据类型
  • String
  • 枚举
  • Class
  • 注解类型
  • 以上类型的一维数组

value属性

如果注解的属性只有一个,且叫value,那么使用该注解时,可以不用指定属性名,因为默认就是给value赋值:

但是注解的属性如果有多个,无论是否叫value,都必须写明属性的对应关系:

数组属性

如果数组的元素只有一个,可以省略{}:


小结

  • 注解就像标签,是程序判断执行的依据。比如,程序读到@Test就知道这个方法是待测试方法,而@Before的方法要在测试方法之前执行
  • 注解需要三要素:定义、使用、读取并执行
  • 注解分为自定义注解、JDK内置注解和第三方注解(框架)。自定义注解一般要我们自己定义、使用、并写程序读取,而JDK内置注解和第三方注解我们只要使用,定义和读取都交给它们
  • 大多数情况下,三角关系中我们只负责使用注解,无需定义和执行,框架会将注解类读取注解的程序隐藏起来,除非阅读源码,否则根本看不到。平时见不到定义和读取的过程,光顾着使用注解,久而久之很多人就忘了注解如何起作用了!

关于注解的使用案例,请参考注解(下)

参考资料:

1.崔希凡JavaWeb

2019-3-31 14:40:15

注解开发不方便_注解(上)相关推荐

  1. java 注解开发_Java中的注解到底是如何工作的?

    作者:人晓 www.importnew.com/10294.html 自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Over ...

  2. 查询blob字段_一次注解开发实战-我使用注解对微服务的跨库查询做了封装

    背景 在开发过程中,你肯定会遇到这样一个场景: "获取订单列表,需要显示订单id,下单人member_id,下单人姓名member_name.数据库订单表只有member_id字段,memb ...

  3. Spring注解开发-初始化/销毁方法注解

    初始化/销毁方法. 初始化:@PostConstruct相当于init-method="myInit" @PreDestory相当于是destroy-method="my ...

  4. Struts2.0第三章(文件上传、ajax开发、json、Fastjson、Jackson、注解开发)

    Struts2.0文件上传: 浏览器端注意事项: 1.表单提交方式method = post: 2.表单中必须有一个<input type="file" name = &qu ...

  5. Spring IOC注解开发

    Spring IOC注解开发 @(Spring)[Spring, ioc, 注解] Spring IOC注解开发 Spring的IOC的注解使用步骤 创建项目引入jar包 引入配置文件 创建相关包和类 ...

  6. 【Java从0到架构师】Spring - 纯注解开发

    纯注解开发 纯注解开发 - AOP 纯注解开发 - 整合 MyBatis 纯注解开发 - 事务管理 JSR 注解 JSR 注解 - @Resource.@Inject JSR 注解 - @PostCo ...

  7. 二、Java框架之Spring注解开发

    文章目录 1. IOC/DI注解开发 1.1 Component注解 @Component @Controller @Service @Repository 1.2 纯注解开发模式 1.3 注解开发b ...

  8. 使用注解开发(重点)

    1.面向接口编程 根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好. 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象 ...

  9. MyBatis07:使用注解开发

    本文为 SSM 框架系列之 MyBatis 第七部分:使用注解开发 其它内容的链接如下: [1]MyBatis01:初识 MyBatis 与 第一个 MyBaits 程序 [2]MyBatis02:C ...

最新文章

  1. Android 白天/夜间模式切换
  2. [导入]郁闷`````[原]
  3. WinApi学习笔记-获取电脑中磁盘信息
  4. JQuery/JS select标签动态设置选中值、设置禁止选择 button按钮禁止点击 select获取选中值...
  5. 【笔记】与Android选项卡一周
  6. sql2012简体中文版安装
  7. 比起掉头发,我更怕掉队
  8. mysql配置文件完全_MySQL配置文件my.cnf详解
  9. linux工程师如何查询时间,查询Linux系统最后重启时间的三个方法
  10. NetLimiter 4.0.15.0 x64 破解新鲜出炉!
  11. OA实施案例:服务性行业如何选型OA系统
  12. 求丑数(判断一个整数是否是丑数)
  13. 一点点读懂regulator(四)
  14. 条形码标签,实现产品追溯的最佳工具
  15. JS判断今天是礼拜几
  16. 阿里云薛冰洋:边缘云自动化测试解决方案—TestMaster
  17. 八、Linux全套大总结
  18. 自定义 ViewGroup,实现多功能流式布局与网格布局
  19. 超酷3D粒子分散PS动作_使用教程
  20. 写一个用矩形法求定积分的通用函数

热门文章

  1. 使用Axis开发Web Service程序
  2. sqlserver之定位死锁(经验分享)
  3. 齐聚上海,get多媒体开发新技能(内附讲师资料下载)
  4. day16-Dom样式操作
  5. “Python来玩微信跳一跳”教程(问题总结)
  6. 洛谷U4727小L的二叉树[树转序列 LIS]
  7. linux syslog用法
  8. 现半透明的popupwindow
  9. 参考: 40个轻量级 JavaScript 库
  10. linux ssh 报错 Write failed: Broken pipe 解决方法