问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Java注解详解和自定义注解实战,用代码讲解

发布网友 发布时间:2024-09-26 16:04

我来回答

1个回答

热心网友 时间:2024-09-27 01:41

关于我为啥突然又想要了解Java注解和反射

好奇心来啦

打算看源码(只是有想法,flag中,实现挺难)

巩固Java基础知识(基础不牢,地动山摇)

一、逻辑思维图?

第 1-5 小节均偏向于理论知识,若只是想要了解如何自定义注解和如何应用注解,请跳转至第6小节开始阅读。

在本篇中,主要是针对注解的概念和运行时注解进行解释说明,附带有三个实战的案例,尽可能的让大家能够理解透彻并且能够加以应用。

二、什么是注解???

Java注解(Annotation)用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java注解是从 Java5 开始添加到 Java 的。--官方文档

2.1、注解

Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。

我们常常使用的注解,@Data、@Controller等等,这些都是注解,创建一个注解,也很简单,创建一个类,然后将class改为 @interface就是一个注解啦。

2.2、注解出现的位置

Java代码中的包、类型、构造方法、方法、成员变量、参数、本地变量的声明都可以用注解来修饰。注解本质上可以看作是一种特殊的标记,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。

2.3、关于注解的处理

我们一般将利用反射来处理注解的方式称之为运行时注解。

另外一种则是编译时注解,如我们常常使用的 lombok 里的注解,@Data,它能够帮我们省略set/get方法,我们在Class上加上这个注解后,在编译的时候,lombok其实是修改了.class文件的,将set/get方法放进去了,不然的话,你可以看看编译完后的.class文件。诸如这种,我们常称为编译时注解,也就是使用Javac处理注解。

--图:来自于极客学院

这幅图就是从.java文件到class文件的,再到class文件被 JVM 加载的过程。

而其中的注解抽象语法树这一阶段,就是去解析注解,然后根据定义的注解处理器进行相关的逻辑处理。

这一块不是我的关注点,略过略过啦,朋友们,好奇可以去研究研究噢

三、注解的目的或作用?

生成文档。这是最常见的,也是 Java 最早提供的注解。如@param、@return等等

跟踪代码依赖性,实现替代配置文件功能。作用就是减少配置,如 Spring中Bean的装载注入,而且现在的框架基本上都是使用注解来减少配置文件的数量,同时这样也使得编程更加简洁,代码更加清晰。

在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;

标识作用。当Java编译时或运行时,检测到这里的注解,做什么的处理,自定义注解一般如此。

携带信息。 注解的成员提供了程序元素的关联信息,Annotation的成员在 Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认 语法:允许声明任何Annotation成员的默认值。一个Annotation可以将name=value对作为没有定义默认值的Annotation成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也 可以被子类覆盖。

这么一大段话,其实就是关于注解中成员的解释。

说了这么多,其实一句话也能表达完。

注解就是一张便利贴,它贴在那里,你看到的那一刻,就明白该做什么事啦。

如出门前,门上贴着一张便利贴?,上面写着"出门记得带钥匙",当你看到的那一刻,你就会去检查一下自己是否带钥匙啦。

在Java中也是一样的,你定义了一个注解,注解上可以写一些东西,然后你再将它贴在某个上面,说明白执行规则,当编译到这里的时候需要干嘛干嘛,又或者是当运行到这里的时候需要干嘛干嘛。

因为注解写的东西的不同,或者是处理注解的规则不同,而产生了不同的注解及作用。

四、JDK内置注解?

Java中 内置的注解有5类,具体包括:

@Deprecated:过时注解,用于标记已过时 & 被抛弃的元素(类、方法等)

@Override:复写注解,用于标记该方法需要被子类复写

@SuppressWarnings:阻止警告注解,用于标记的元素会阻止编译器发出警告提醒

@SafeVarargs:参数安全类型注解,用于提醒开发者不要用参数做不安全的操作 & 阻止编译器产生 unchecked警告,Java 1.7 后引入

五、元注解 ?

何为元注解?就是注解的注解,就是给你自己定义的注解添加注解,你自己定义了一个注解,但你想要你的注解有什么样的功能,此时就需要用元注解对你的注解进行说明了。

接着上一个比喻

注解有很多很多吗,门上贴一个,冰箱上贴一个,书桌上贴一个等等

元注解勒就是把他们整合起来称呼的,像上面这些可以统称为生活类注解啊。所以也就是注解的注解。

5.1、@Target

在 @Target 注解中指定的每一个 ElementType 就是一个约束,它告诉编译器,这 个自定义的注解只能用于指定的类型。

说明了注解所修饰的对象范围:注解可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。

5.2、@Retention

定义了该注解的生命周期:

某些注解仅出现在源代码中,而被编译器丢弃; (源码级)

而另一些却被编译在class文件中; (字节码级)

编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为注解与class在使用上是被分离的)。绝大多数开发者都是使用RUNTIME,因为我们期望在程序运行时,能够获取到这些注解,并干点有意思的事儿,而只有RetentionPolicy.RUNTIME,能确保自定义的注解在运行时依然可见。(运行级)

使用这个元注解可以对自定义注解的“生命周期”进行*。

RetentionPolicy.SOURCE 一般开发者很少用到,大都是Java内置的注解。如@Override

@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {

这些注解只是在编译的时候用到,一旦编译完成后,运行时没有任何意义,所以他们被称作源码级别注解。

如果有了解过 lombok 一些简单原理的开发者, 都知道它是通过注解在编译时自动生成一部分代码,让源码看起来更简洁,字节码却很强大。

当然,这种方式有它自身的缺陷,譬如不一致性,问题排解时的困扰,以及依赖问题,不是本篇重点,扯回来。

提供信息给编译器: 编译器可以利用注解来检测出错误或者警告信息,打印出日志。

编译阶段时的处理: 软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理。

运行时处理: 某些注解可以在程序运行的时候接受代码的提取,自动做相应的操作。

5.3、@Documented

用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如 javadoc此类的工具文档化。是一个标记注解,没有成员。

5.4、@Inherited

是一个标记注解阐述了某个被标注的类型是被继承的。使用了@Inherited修饰的注解类型被用于一个class时该class的子类也有了该注解。

5.5、@Repeatable

允许一个注解可以被使用一次或者多次(Java 8)。

六、自定义注解?

自定义注解实际上就是一种类型而已,也就是引用类型(Java中除了8种基本类型之外,我们见到的任何类型都是引用类型)

6.1、定义注解

自定义注解过程:

声明一个类MyAnnotation

把class关键字改为@interface

这样我们就声明了一个自定义的注解,当我们用@interface声明一个注解的时候,实际上是声明了一个接口,这个接口自动的继承了java.lang.annotation.Annotation,但是我们只需要@interface这个关键字来声明注解,编译器会自动的完成相关的操作,不需要我们手动的指明继承Annotation接口

另外在定义注解时,不能再继承其他的注解或接口。

我举了四个例子,这四个注解分别是放在 类(接口、枚举类上)、构造函数、方法级别、成员属性上的。

@Documented//定义可以被文档工具文档化@Retention(RetentionPolicy.RUNTIME)//声明周期为runtime,运行时可以通过反射拿到@Target(ElementType.TYPE)//注解修饰范围为类、接口、枚举public @interface ClassAnnotation {public String name() default "defaultService";public String version() default "1.1.0";}@Documented@Target(ElementType.CONSTRUCTOR)@Retention(RetentionPolicy.RUNTIME)public @interface ConstructorAnnotatin {String constructorName() default "";String remark() default "构造器";}@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface FieldAnnotation {public String name() default "defaultName";public String value() default "defaultValue";} @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodAnnotation { public String name() default "defaultName"; public MethodTypeEnum type() default MethodTypeEnum.TYPE1; }public enum MethodTypeEnum {TYPE1,TYPE2}6.2、注解的成员变量

成员以无参数无异常的方式声明 String constructorName() default "";

可以使用default为成员指定一个默认值public String name() default "defaultName";

成员类型是受限的,合法的类型包括原始类型以及String、Class、Annotation、Enumeration (JAVA的基本数据类型有8种:byte(字节)、short(短整型)、int(整数型)、long(长整型)、float(单精度浮点数类型)、double(双精度浮点数类型)、char(字符类型)、boolean(布尔类型)

public MethodTypeEnum type() default MethodTypeEnum.TYPE1;

注解类可以没有成员,没有成员的注解称为标识注解,例如JDK注解中的@Override、@Deprecation

如果注解只有一个成员,并且把成员取名为value(),则在使用时可以忽略成员名和赋值号“=”

例如JDK注解的@SuppviseWarnings ;如果成员名 不为value,则使用时需指明成员名和赋值号"="

6.3、使用注解

因为我们在注解中声明了属性,所以在使用注解的时候必须要指明属性值 ,多个属性之间没有顺序,多个属性之间通过逗号分隔

@ClassAnnotation(name = "personBean", version = "1.2.1")public class Person {//告诉大家是可以用的,但是影响我测试,我就又注释掉了.//@ConstructorAnnotatin(constructorName="Person()")//public Person(String description) {//this.description = description;//}@FieldAnnotation(name = "description", value = "This is my personal annotation")private String description;public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}@MethodAnnotation(name = "sayHello", type = MethodTypeEnum.TYPE2)public void sayHello() {System.out.println("Hello Annotation!");}}6.4、浅提一下反射

想要去获取注解就不得不提到反射啦,但 Java 反射会带来一定的耗时,因此使用运行注解需要考虑对性能的影响。

我们声明一个Student类用来描述学生对象的信息的

class Student{ String name; String school; //...set/get}

当我们创建一个学生对象时,学生对象的信息是保存在 Student 类中,所以 Student 类会提供获取这些信息的方法。

在Java类中,每个类都会有对应的Class,要想执行反射操作,必须先要获取指定类名的Class

了解Class对象:

类是程序的一部分,每个类都有一个 Class 对象。换言之,每当我们编写并且编译 了一个新类,就会产生一个 Class 对象(更恰当的说,是被保存在一个同名的 .class 文件中)。为了生成这个类的对象,Java 虚拟机 (JVM) 先会调用 “类加载器” 子系统把 这个类加载到内存中。

Class类:简单说就是用来描述类对象的信息的

类对象的信息包括:

类的基本信息:包名、修饰符、类名、基类,实现的接口

属性的信息:修饰符、属性类型、属性名称、属性值,

方法的信息:修饰符、返回类型、方法名称、参数列表、抛出的异常

构造方法的信息:修饰符、类名、参数列表、抛出的异常

注解的相关信息:

因为:类对象的相关信息全部保存在Class类

所以:Class类会提供获取这些信息的方法

一旦某个类的 Class 对象被载入内存,它就可以用来创建这个类的所有对象。

通过 Class 获取类的相关信息,通过Class创建对象,通过 Class 调用对象上面的属性,调用对象上面的方法,这种操作就称为反射,要想执行反射操作,必须先要获取到指定的类名的 Class

获取Class的不同方式

获取基本类型的Class

1)基本类型Class:如 int.Class获取的就是 int 类型的 Class

获取引用类型的Class:

1)引用类型的Class:如String.Class获取的就是String类对应的Class

2)通过对象来获取:如:String obj="hello",Class calz = obj.getClass(),获取的就是String类对应的Class

3)Class.forName("java.lang.String"),获取的就是对应的Class

6.5、提取注解public class TestClassAnnotation {private static Person person = new Person();public static void main(String[] args) {Class<?> clazz = person.getClass();//因为注解是作用于类上面的,所以可以通过isAnnotationPresent来判断是否是一个具有指定注解的类if (clazz.isAnnotationPresent(ClassAnnotation.class)) {System.out.println("This is a class with annotation ClassAnnotation!");//通过getAnnotation可以获取注解对象ClassAnnotation annotation = clazz.getAnnotation(ClassAnnotation.class);if (null != annotation) {System.out.println("BeanName = " + annotation.name());System.out.println("BeanVersion = " + annotation.version());} else {System.out.println("the annotation that we get is null");}} else {System.out.println("This is not the class that with ClassAnnotation");}}}@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {0@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {1@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {2七、自定义注解实战???

注解大多时候与反射或者 AOP 切面结合使用,它的作用有很多,比如标记和检查,最重要的一点就是简化代码,降低耦合性,提高执行效率。

7.1、自定义注解 + SpringMVC *实现权限控制功能

还有一种应用场景,权限判断或者说是登录校验。

这个是我当时还没有学习市面上的权限框架,就是使用了这种自定义注解+*的方式来实现简单的权限控制。

注意:此案例不可CV直接运行,代码很容易实现,大家理解思路即可。

定义注解:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {3@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {4

使用注解:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {5@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {6

编写 SpringMVC*及处理注解的Handler

在其中进行 Token 的判断,和访问方法的权限判断,看方法上是否有注解,有的话,

就和当前用户对比,成功就可以访问,失败就直接拒绝。

当时用的是SSM框架,所以才会看到有 response.sendRedirect(contextPath + "/login");这样的。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {7

用于检查 方法 或者 类 是否需要权限

并和 拥有的权限做对比

如果方法上有 ,则以方法的 优先

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(Retent
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
0.51除以0.5列竖式怎么计算呢? 支付宝辅助解封安全吗 虎牙直播手机版怎么发弹幕 虎牙直播开启弹幕教程 步步高手机s11t可以云储存嘛? 步步高s11t手机老是显示x空间停用 我的手机是步步高vivos11t的,我把手机信息加密了怎样才可以取消_百度知 ... 步步高手机s11tsd卡取出后再放进手机里就说存储模式不可用,怎么办? 意大利音乐学院硕士生含金量如何 世界音乐学院排名新鲜出炉!你向往哪一所? 南京航海技术学校怎么样 类加载过程与双亲委派机制 我想问下,女生们会觉得男生这个发型不好看吗?比较非主流,还是男生留刘 ... 一个女的很爱这个男的,男的也一样,但是突然有一天男生的发型变丑... ...不要开虚拟是什么意思?好多人都建议先从虚拟开始。难道另有说法_百... 开网店做虚拟与实物哪个赚钱 淘宝创业都说先做虚拟再买实物,但是为什么我见到的做虚拟的基本上都一直... 开网店做虚拟还实物 刚开淘宝店,我不能做实物吗,得做虚拟的吗?都说要做虚拟得花300买软件... 甲亢吃中药能治愈吗 ...一到夏天活动一下就流很多汗,现在晚上睡觉有时也出 头部盗汗是啥原因 男人晚上睡觉老出虚汗怎么回事? CV、 Resume、 Coverletter有什么区别? 小型面包车一般开多少公里换机油比车交好? 面包车发动机四年三万公里没换机油了,该怎么处理 请问下面每行中的数分别是多少的几倍? 我的面包车前段时间去做保养,没有去4S店,是在外面的门店保养的,保养的... 我的是IBMR52 的 我的电脑休眠以后怎么按 空格键和栋鼠标都不行 的 要... 我买了IBMR52用了2年,我把电池卸了,现在开机慢的不的了,要3分钟,请问... 黄泥坳地址在哪里? 标签选择器和类选择器优先级? 山东柳琴戏传统剧目 烹饪笋子烧牛排时有哪些能保持口感的小技巧? 牛肉跟什么搭配烧菜好吃 年终总结的开头和结束语精选文档 是不是得不到的才是最珍贵的 世界上最珍贵的是得不到和已失去 人一生得到最珍贵的东西有什么?拜托各位了 3Q 脚崴伤如何快速恢复 有一首歌歌词是,你的爱会将我灌醉,我没有所谓,一个女人唱的声音很... 那个流行歌曲里有这样一句歌词“你的爱 会将我灌醉 我没有所谓 ”大概... 笔记本怎么进入bios?笔记本进入bios方法【详解】-搜狗输入法_百度知 ... 歌词你的爱没将我灌醉我没有所谓好爱这种感觉这是啥歌 哪些角度是指的什么方面 抖音你的爱会将我灌醉我没有所谓是什么歌介绍_抖音你的爱会将我灌醉我... 电脑一开机就进BIOS,进不了系统,重装系统光盘,u盘都读不出来,并且我已... 有没有哪位高人是毕业了的英语专业的研究生,你现在都在做什么工作呀 英语专业的研究生平时都在做什么呢? 大概要学几年,什么时候找工作实习... ...的英语研究生容易进出版社工作,能做些什么工作,还有出版社的待遇好... F-1和J-1学生签证有什么区别