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

java 5 注解机制_Java Annotaions?(注解)的本质和实现原理(上)

引言

曾几何时,XML 一直是 Java 各大框架配置元数据(meta data) 的主要途径。但作为一种集中式的元数据管理工具,配置项与作用代码距离太过 “遥远”,非常不利于代码的维护和调试。再加上 XML 本身复杂的语法结构,往往令码农们大感头疼。一种与作用代码耦合在一起的元数据配置方式呼之欲出。于是 注解 (Annotations)就在 JDK 5 中正式出现在开发者的视线之中了。

日常使用 Spring Boot 的同学对于注解的使用肯定再熟悉不过了。但大家有没有想过这些注解,例如 @Configuration @Component @Bean 等等之类的,其本质到底是什么,又是怎样发挥自己特定的作用呢?

注解的本质

oracle 官网对注解的定义为:

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.注解是元数据的一种形式,它提供有关程序的数据,该数据不属于程序本身。 注解对其注释的代码操作没有直接影响。

从这个定义里我们可以看出,首先注解携带的是元数据,其次,它可能会引起一些和元数据相关的操作,但不会对被注释的代码逻辑产生影响。

而在JDK的Annotation接口中有一行注释如此写到:

/**

* The common interface extended by all annotation types.

* ...

*/

public interface Annotation {...}

复制代码

这说明其他注解都扩展自 Annotation 这个接口,也就是说注解的本质就是一个接口。

以 Spring Boot 中的一个注解为例:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Indexed

public @interface Component {

String value() default "";

}

复制代码

它实际上相当于:

public interface Component extends Annotation{...}

复制代码

而@interface 可以看成是一个语法糖。

注解的要素

我们依然来看 @Component 这个例子:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Indexed

public @interface Component {

String value() default "";

}

复制代码

在注解定义上有几个注解@Target, @Retention, @Documented,被称为 元注解。

所谓元注解就是说明注解的注解,这就好比 Python 中的元类。

Java 中的元注解共有以下几个:

@Target

顾名思义,这个注解标识了被修饰注解的作用对象。我们看看它的源码:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Target {

/**

* Returns an array of the kinds of elements an annotation type

* can be applied to.

* @return an array of the kinds of elements an annotation type

* can be applied to

*/

ElementType[] value();

}

复制代码

可以看到,这个注解的 value 值是一个数组,这也就意味着注解的作用对象可以有多个。

其取值范围都在ElementType这个枚举之中:

public enum ElementType {

/** 类、接口、枚举定义 */

TYPE,

/** 字段,包括枚举值 */

FIELD,

/** 方法 */

METHOD,

/** 参数 */

PARAMETER,

/** 构造方法 */

CONSTRUCTOR,

/** 局部变量 */

LOCAL_VARIABLE,

/** 元注解 */

ANNOTATION_TYPE,

/** 包定义 */

PACKAGE...

}

复制代码

不同的值代表被注解可修饰的范围,例如TYPE只能修饰类、接口和枚举定义。这其中有个很特殊的值叫做 ANNOTATION_TYPE, 是专门表示元注解的。

在回过头来看 @Component 这个例子, Target 取值为 TYPE。熟悉 Spring Boot 的同学也一定知道,@Component 确实是不能放到方法或者属性前面的。

@Retention

@Retention 注解指定了被修饰的注解的生命周期。定义如下:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Retention {

/**

* Returns the retention policy.

* @return the retention policy

*/

RetentionPolicy value();

}

复制代码

可以看到这个注解带一个 RetentionPolicy 的枚举值:

public enum RetentionPolicy {

SOURCE,

CLASS,

RUNTIME

}

复制代码SOURCE 表示注解编译时可见,编译完后就被丢弃。这种注解一般用于在编译器做一些事情;

CLASS 表示在编译完后写入 class 文件,但在类加载后被丢弃。这种注解一般用于在类加载阶段做一些事情;

RUNTIME 则表示注解会一直起作用。

@Documented

这个注解比较简单,表示是否添加到 java doc 中。

@Inherited

这个也比较简单,表示注解是否被继承。这个注解不是很常用。

注意:元注解只在定义注解时被使用!

注解的架构

从上面的元注解可以了解到,一个注解可以关联多个 ElementType,但只能有一个 RetentionPolicy:

9cdc892c96250bde5c3c69bc1f598089.png

Java 内置注解

Java 中有三个常用的内置注解,其实相信大家都用过或者见过。不过在了解了注解的真实面貌以后,不妨重新认识一下吧!

@Override

它的定义为:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

复制代码

可见这个注解没有任何取值,只能修饰方法,而且RetentionPolicy 为 SOURCE,说明这是一个仅在编译阶段起作用的注解。

它的真实作用想必大家一定知道,就是在编译阶段,如果一个类的方法被 @Override 修饰,编译器会在其父类中查找是否有同签名函数,如果没有则编译报错。可见这确实是一个除了在编译阶段就没什么用的注解。

@Deprecated

它的定义为:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

public @interface Deprecated {

}

复制代码

这个注解也没有任何取值,能修饰所有的类型,永久存在。这个注解的作用是,告诉使用者被修饰的代码不推荐使用了,可能会在下一个软件版本中移除。这个注解仅仅起到一个通知机制,如果代码调用了被@Deprecated 修饰的代码,编译器在编译时输出一个编译告警。

@SuppressWarnings

它的定义为:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings {

/**

* The set of warnings that are to be suppressed by the compiler in the

* annotated element. Duplicate names are permitted. The second and

* successive occurrences of a name are ignored. The presence of

* unrecognized warning names is not an error: Compilers must

* ignore any warning names they do not recognize. They are, however,

* free to emit a warning if an annotation contains an unrecognized

* warning name.

*

*

The string {@code "unchecked"} is used to suppress

* unchecked warnings. Compiler vendors should document the

* additional warning names they support in conjunction with this

* annotation type. They are encouraged to cooperate to ensure

* that the same names work across multiple compilers.

* @return the set of warnings to be suppressed

*/

String[] value();

}

复制代码

这个注解有一个字符串数组的值,需要我们使用注解的时候传递。可以在类型、属性、方法、参数、构造函数和局部变量前使用,声明周期是编译期。

这个注解的主要作用是压制编译告警的。

例如

public static void main(String[] args) {

Date date = new Date(2020, 5, 22);

}

复制代码

我们可以看到,Date 的这个构造函数是被@Deprecated 修饰的:

@Deprecated

public Date(int year, int month, int date) {

this(year, month, date, 0, 0, 0);

}

复制代码

所以上面的代码在编译时会报一个Warning:

java: java.util.Date 中的 Date(int,int,int) 已过时

复制代码

为了不让编译器输出这个 Warning, 就需要在上述的 main 方法前面增加一个 @SuppressWarnings 注解:

@SuppressWarning(value = "deprecated")

public static void main(String[] args) {

Date date = new Date(2020, 5, 22);

}

复制代码

注解输入的字符串deprecated 表明让编译器忽略 @Deprecated注解引发的编译告警。

总结和预告

本篇主要介绍了 Java 注解的本质、要素以及元注解、三种 Java 内置注解。

下篇我们将站在JVM的角度深入分析注解的功能实现原理,并手写一个自己的注解。

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[Java Annotaions?(注解)的本质和实现原理(上)]http://www.zyiz.net/tech/detail-137538.html

相关文章:

  • java hibernate批量更新_使用HQL查询进行Hibernate批量更新
  • 火焰纹章2java_火焰之纹章2手机java版有秘籍么?
  • java 二进制读写文件操作_Java 二进制文件读写操作
  • java中除去双字节空格的问题_Java去除字符串中的空格
  • java 在指定位置添加字符_Java——在指定位置拼接和插入字符串
  • java类对象初始化_Java类的初始化和对象的创建
  • mysql 暴库 group_concat()_MYSQL数据库***之Group_concaT函数终极利用
  • java 对象 传递_Java 的对象传递
  • vue 限制渲染条数_Vue 2.x 假分页处理数据量过多导致页面渲染慢的问题
  • git用户名和密码保存文件_Git - 凭证存储
  • java 五子棋项目_Java项目实现五子棋小游戏
  • java swt 画按钮_java – SWT透明按钮/标签
  • 哪个软件可以玩java游戏_安卓java模拟器?安卓手机如何玩JAVA游戏以及JAVA软件的方法...
  • java 运行模式_浅谈Tomcat三种运行模式
  • java反射i_Java反射
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • [deviceone开发]-do_Webview的基本示例
  • Elasticsearch 参考指南(升级前重新索引)
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Linux gpio口使用方法
  • Linux后台研发超实用命令总结
  • Making An Indicator With Pure CSS
  • Netty源码解析1-Buffer
  • React-redux的原理以及使用
  • spark本地环境的搭建到运行第一个spark程序
  • win10下安装mysql5.7
  • windows下mongoDB的环境配置
  • 第十八天-企业应用架构模式-基本模式
  • 给新手的新浪微博 SDK 集成教程【一】
  • 构造函数(constructor)与原型链(prototype)关系
  • 使用Gradle第一次构建Java程序
  • 微信开源mars源码分析1—上层samples分析
  • Linux权限管理(week1_day5)--技术流ken
  • #Java第九次作业--输入输出流和文件操作
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (八)c52学习之旅-中断实验
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (一)UDP基本编程步骤
  • (转)memcache、redis缓存
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • *p++,*(p++),*++p,(*p)++区别?
  • .NET Reactor简单使用教程
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析
  • @private @protected @public
  • [Android学习笔记]ScrollView的使用
  • [Angularjs]ng-select和ng-options
  • [C#]手把手教你打造Socket的TCP通讯连接(一)
  • [C]编译和预处理详解
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!
  • [C语言]——内存函数
  • [HCTF 2018]WarmUp (代码审计)
  • [Hive] CTE 通用表达式 WITH关键字
  • [InnoDB系列] -- SHOW INNODB STATUS 探秘