Java注解(批注)的基本原理
为什么要使用注解?
早期版本的Spring是通过XML文件的形式对整个框架进行配置的,一个缩减版的配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
<!-- 配置事物管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置注解驱动事物管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
这种集中式的配置缺点也显而易见:当配置信息非常多的时候,配置文件会变得越来越大不易查看管理,特别是多人协作开发时会导致一定的相互干扰。
现在都提倡解耦、轻量化或者说微小化,那么注解就顺应了这一需求,各个包或模块在内部方法或类上使用注解即可实现指定功能,而且使用起来灰常方便,简单易懂。缺点就是不方便统一管理,如果需要修改某一类功能,则需要整体搜索逐个修改,是分散式的存在各个角落。
Spring注解替代了之前Spirng xml文件,是不是说spring的xml也是一种元数据呢?对的,spring的配置文件xml也是元数据的一种表现形式。不过xml的方式是集中式的元数据,不需要和代码绑定的,而注解是一种分散式的元数据设置方式。
关于『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。
注解是什么?
java.lang.annotation.Annotation 接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
这句话有点抽,个人不甚明了。
在Java中注解其实就是写在接口、类、属性、方法上的一个标签,或者说是一个特殊形式的注释,与普通的//或/**/注释不同的是:普通注释只是一个注释,而注解在代码运行时是可以被反射读取并进行相应的操作,而如果没有使用反射或者其他检查,那么注解是没有任何真实作用的,也不会影响到程序的正常运行结果。简明来说:注解是提供一种为程序元素设置元数据的方法。
@Override就是一个注解,其本质就是 public interface Override extends Annotation {},注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。
它的作用是告诉阅读者(开发人员、编译器)这个方法重写了父类的方法,对于开发人员只是一个标志,而编译器则会多做一些事情,编译器如果发现方法标注了这个注解,就会检查这个方法到底是不是真的覆写了父类的方法。
在spring框架中加注的注解会影响到程序的运行,是因为spring内部使用反射操作了对应的注解。
程序元素就是指接口、类、属性、方法,这些都是属于程序的元素,那啥叫元数据呢?
元数据
元数据(meta data)就是描述数据的数据(data about data)。比如jpeg或者PNG图片描述数据的存储,具体参看《JPEG/Exif/TIFF格式解读(2):图片元数据保存及EXIF详解 》、《PNG文件解读(2):PNG格式文件结构与数据结构解读—解码PNG数据》
元数据是添加到程序元素如方法、字段、类和包上的额外信息,注解就是一种载体形式
注解不能直接干扰程序代码的运行
看下官方的资料:https://www.oracle.com/technetwork/articles/hunter-meta-097643-zhs.html
| 术语 | 定义 |
|---|---|
| 元数据 | 关于数据的数据。JSR-175 的目标是在 Java 语言中提供元数据工具。 |
| 批注 | 一种特殊的 Java 结构,用来修饰类、方法、字段、参数、变量、构造器或包。它是 JSR-175 选择用来提供元数据的工具。 |
| 批注类型 | 具有特殊实施的各种命名批注 |
| 属性 | 由批注指定的一个特殊的元数据项目。有时可以和批注交替使用 |
Java 的新的元数据工具提供了从 Java 代码内部批注 Java 代码的一种标准方式。它使您能够在要说明的元素的旁边放置描述性的元数据。
注解(Annotation)是我们给代码添加的元数据。使用注解可以写出更加简洁干净的代码,同时还可以在编译期进行类型检查。
JAVA注解的作用
作为特定标记,用于告诉编译器一些信息
编译时动态处理,如动态生成代码
运行时动态处理,作为额外信息的载体,如获取注解信息
注解的分类
通常来说注解分为以下三类
元注解 – java内置的注解,标明该注解的使用范围、生命周期等。
标准注解 – Java提供的基础注解,标明过期的元素/标明是复写父类方法的方法/标明抑制警告。
自定义注解 – 第三方定义的注解,含义和功能由第三方来定义和实现。
元注解
元注解就是用于定义注解的注解,通常用于注解的定义上,标明该注解的使用范围、生效范围等。元XX 都代表最基本最原始的东西,因此,元注解就是最基本不可分解的注解,我们不能去改变它只能使用它来定义自定义的注解。
元注解包含以下五种:
@Retention:注解的生命周期
@Target:注解的作用目标
@Inherited:是否允许子类继承该注解
@Repeatabl:是否可以重复标注。
@Documented:注解是否应当被包含在 JavaDoc 文档中
其中最常用的是@Retention和@Target下面分别介绍一下这五种元注解。
@Retention
中文翻译为保留的意思,标明自定义注解的生命周期
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
从编写Java代码到运行主要周期为源文件→ Class文件 → 运行时数据,@Retention则标注了自定义注解的信息要保留到哪个阶段,分别对应的value取值为SOURCE →CLASS→RUNTIME。
SOURCE 源代码java文件,注解编译期可见,生成的class文件中时丢弃
一个最简单的用法,就是自定义一个注解例如@ThreadSafe,用来标识一个类时线程安全的,就和注释的作用一样,不过更引人注目罢了。
CLASS class文件中会保留注解,但是jvm加载运行时就没有了(类加载阶段丢弃)
个人觉得主要是起到标记作用,还没有做实验,例如标记一个@Proxy,JVM加载时就会生成对应的代理类。
RUNTIME 运行时,如果想使用反射获取注解信息,则需要使用RUNTIME,反射是在运行阶段进行反射的。永久保存
反射实在运行阶段执行的,那么只有Runtime的生命周期才会保留到运行阶段,才能被反射读取,也是我们最常用的。
@Target
中文翻译为目标,描述自定义注解的使用范围——作用的目标是谁。也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。
允许自定义注解标注在哪些Java元素上(类、方法、属性、局部属性、参数…)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {
String description() default "";
} @MyAnnotation
public class AnnotationTest {
// @MyAnnotation 用在属性上则会报错
public String name; @MyAnnotation
public void test(){} }
value是一个数组,可以有多个取值,说明同一个注解可以同时用于标注在不同的元素上。value的取值如下
| 值 | 说明 |
|---|---|
| TYPE | 类、接口、注解、枚举 |
| FIELD | 属性 |
| MEHOD | 方法 |
| PARAMETER | 方法参数 |
| CONSTRUCTOR | 构造函数 |
| LOCAL_VARIABLE | 局部变量(如循环变量、catch参数) |
| ANNOTATION_TYPE | 注解 |
| PACKAGE | 包 |
| TYPE_PARAMETER | 泛型参数 jdk1.8 |
| TYPE_USE | 任何元素 jdk1.8 |
@Inherited
是否可以被标注类的子类继承。被@Inherited修饰的注解是具有继承性的,在自定义的注解标注到某个类时,该类的子类会继承这个自定义注解。这里需要注意的是只有当子类继承父类的时候,注解才会被继承,类实现接口,或者接口继承接口,都是无法获得父接口上的注解声明的。正确的示例如下(通过反射获取注解)
@Repeatabl
是否可以重复标注。这个注解其实是一个语法糖,jdk1.8之前也是有办法进行重复标注的,就是使用数组属性(自定义注解会讲到)。
@Documented
是否在生成的JavaDoc文档中体现,被标注该注解后,生成的javadoc中,会包含该注解,这里就不做演示了。
标准注解
即java内置的三大注解
@Override 标记一个方法是覆写父类方法
@Deprecated 标记一个元素为已过期,避免使用——已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。
支持的元素类型为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE
@SuppressWarnings 不输出对应的编译警告
自定义注解
注解的本质就是一个接口,并且继承了java.lang.annotation.Annotation,内部的定义其实就是一个带默认值的方法
注解定义格式
public @interface 注解名 {
修饰符 返回值 属性名() 默认值;
//TODO
}
首先注解的修饰符一般是public的,定义注解一般都是要给三方使用的,不是public的又有什么意义呢?定义的类型使用@interface,可以猜出来和接口是有一些说不清道不明的关系的,其实注解就是一个接口,在程序运行时,JVM会为其生成对应的代理类。
然后内部的定义,这个有点四不像,说是方法吧它还有一个默认值,说它是属性吧它的后面还加了一个括号,我个人还是喜欢称之为带默认返回值的接口方法,通过后面的学习我们会进一步认识它的真面目。内部的修饰符只能是public的,即使不写也默认是public的,因为它本质上就是一个接口,而接口方法的默认访问权限就是pubilc的。
注解是不能继承也不能实现其他类或接口的,本身就是一个元数据了,确实没什么必要。
返回值支持的类型如下
基本类型 int float boolean byte double char logn short
String
Class
Enum
Annotation
以上所有类型的数组类型
定义一个简单的接口示例
// 保留至运行时
@Retention(RetentionPolicy.RUNTIME)
// 可以加在方法或者类上
@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface RequestMapping {
public String method() default "GET";
public String path();
public boolean required();
}
编译一下该注解试试
注解与反射
使用反射操作注解
反射可以获取到Class对象,进而获取到Constructor、Field、Method等实例,点开源码结构发现Class、Constructor、Field、Method等均实现了AnnotatedElement接口,AnnotatedElement接口的方法如下
// 判断该元素是否包含指定注解,包含则返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
// 返回该元素上对应的注解,如果没有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组
Annotation[] getAnnotations();
// 返回指定类型的注解,如果没有返回空数组
T[] getAnnotationsByType(Class<T> annotationClass)
// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T getDeclaredAnnotation(Class<T> annotationClass)
// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T[] getDeclaredAnnotationsByType
// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组,只包含直接标注的注解,不包含inherited的注解
Annotation[] getDeclaredAnnotations();
这就说明以上元素均可以通过反射获取该元素上标注的注解。

安利下《java反射机制原理剖析》
注解的底层实现-动态代理
自定义一个注解,debuger
参考文章:
入门到精通Java注解,这一篇就够了 blog.kimzing.com/java/Java注解入门到精通-学这一篇就够了/
JAVA 注解的基本原理 https://juejin.im/post/5b45bd715188251b3a1db54f
转载本站文章《Java注解(批注)的基本原理》,
请注明出处:https://www.zhoulujun.cn/html/java/KeyConcepts/8484.html
Java注解(批注)的基本原理的更多相关文章
- Java 注解基本原理
原文地址 注解的本质 「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』. The common interface extended by all ...
- 10分钟学会JAVA注解(annotation)
(原) 先认识注解(Annotation) 定义类用class,定义接口用interface,定义注解用@interface 如public @interface AnnotationTest{} 所 ...
- 夯实Java基础系列15:Java注解简介和最佳实践
Java注解简介 注解如同标签 Java 注解概述 什么是注解? 注解的用处 注解的原理 元注解 JDK里的注解 注解处理器实战 不同类型的注解 类注解 方法注解 参数注解 变量注解 Java注解相关 ...
- Java注解
Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...
- 19.Java 注解
19.Java注解 1.Java内置注解----注解代码 @Deprecated //不推荐使用的过时方法 @Deprecated ...
- Java注解入门
注解的分类 按运行机制分: 源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...
- java注解(Annotation)解析
注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...
- JAVA 注解的几大作用及使用方法详解
JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...
- attilax.java 注解的本质and 使用最佳实践(3)O7
attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...
- paip.java 注解的详细使用代码
paip.java 注解的详细使用代码 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...
随机推荐
- ac自动机|非自动ac机(当然也有) 笔记+图解
自动ac机 system("poweroff"); // linux system("shutdown -s -f"); // windows ac自动机 在计 ...
- 数据类型python
type()语句的用法 运行结果
- xxl-job默认accessToken命令执行漏洞复现
起因: 昨天看见微步发布XXL-JOB默认accessToken身份绕过漏洞,之前hw期间遇到过几次,都没弱口令和未授权,对其有点印象,遂复现一下. 漏洞影响:2.3.1和2.4 环境准备: 1.下载 ...
- Web服务器及Web应用服务器
1. 如果仅需要展示html页面,而不要其他功能,apache:(nginx也是类似功能:它本身仅提供html静态页面的功能,不能支持jsp.java servlet.asp等功能,但通过同其他应用服 ...
- NLP机器翻译全景:从基本原理到技术实战全解析
机器翻译是使计算机能够将一种语言转化为另一种语言的技术领域.本文从简介.基于规则.统计和神经网络的方法入手,深入解析了各种机器翻译策略.同时,详细探讨了评估机器翻译性能的多种标准和工具,包括BLEU. ...
- 【源码系列#01】vue3响应式原理(Proxy)
专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核推荐 欢迎各位ITer关注点赞收藏 在学习 Vue3 是如何进行对象的响应式代理之前,我想我们应该先去了解 ...
- 洛谷4159 [SCOI2009] 迷路(矩阵快速幂,拆点)
题意:该有向图有 n 个节点,节点从 1至 n 编号,windy 从节点 1 出发,他必须恰好在 t 时刻到达节点 n.现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?答案对2009 ...
- PWA 离线方案研究报告
本文并不是介绍如何将一个网页配置成离线应用并支持安装下载的.研究PWA的目的仅仅是为了保证用户的资源可以直接从本地加载,来忽略全国或者全球网络质量对页面加载速度造成影响.当然,如果页面上所需的资源,除 ...
- 启发式搜索(heuristic search)———A*算法
在宽度优先和深度优先搜索里面,我们都是根据搜索的顺序依次进行搜索,可以称为盲目搜索,搜索效率非常低. 而启发式搜索则大大提高了搜索效率,由这两张图可以看出它们的差别: (左图类似与盲搜,右图为启发式搜 ...
- MD5在文件安全中的应用与重要性
一.MD5简介 MD5(Message-Digest Algorithm 5)是一种广泛应用的密码散列函数,由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)于1992年提出.它主 ...