JAVA 注解搜索工具类与注解原理讲解(获取方法和类上所有的某个注解,父类继承的注解也支持获取)

文章目录

  • JAVA 注解搜索工具类与注解原理讲解(获取方法和类上所有的某个注解,父类继承的注解也支持获取)
    • 代码
    • 测试
      • 方法上加注解,类上不加
      • 类上加注解、方法上加注解
    • 注解原理
    • 性能测试

JAVA 注解搜索工具类与注解原理讲解(获取方法和类上所有的某个注解,父类继承的注解也支持获取)

基于Spring的AnnotatedElementUtils工具,支持从当前类、父类、接口搜索(支持限制最大深度),支持传入搜索配置、启用缓存。

在这里插入图片描述

代码

import cn.hutool.core.collection.CollectionUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.core.annotation.AnnotatedElementUtils;import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;/*** @author chenfuxing* date: 2024/6/19* description: 注解工具类**/
public class AnnotationUtil extends AnnotatedElementUtils {/*** 默认注解搜索配置*/private static final AnnotationSearchConfig DEFAULT_SEARCH_CONFIG = new AnnotationSearchConfig();/*** 缓存*/private static final Map<String, Set<? extends Annotation>> CACHE = new ConcurrentHashMap<>();/*** 注解搜索配置*/@Data@Accessors(chain = true)@AllArgsConstructor@NoArgsConstructor@ToStringpublic static class AnnotationSearchConfig {/*** 从类上搜索注解*/private boolean searchFromClass = true;/*** 从父类上搜索注解*/private boolean searchSuperClass = true;/*** 从父类上搜索注解的最大深度*/private int searchSuperMaxDepth = 3;/*** 从类的接口上搜索注解*/private boolean searchFromInterfaces = true;/*** 允许使用缓存*/private boolean enableCache = true;}/*** 获取缓存key** @param element* @param annotationType* @param config* @return*/public static String getCacheKey(AnnotatedElement element, Class<?> annotationType, AnnotationSearchConfig config) {StringBuilder sb = new StringBuilder();sb.append(element != null ? element.toString() : "null");sb.append("-");sb.append(annotationType != null ? annotationType.getName() : "null");sb.append("-");sb.append(config != null ? config.toString() : "null");return sb.toString();}/*** 获取注解* 若有多个则只会返回第一个注解(方法上的优先于类上的)** @param element* @param annotationType* @param <A>* @return*/public static <A extends Annotation> A getAnnotation(AnnotatedElement element, Class<A> annotationType) {Set<A> allAnnotations = getAllAnnotations(element, annotationType);return CollectionUtil.isEmpty(allAnnotations) ? null : allAnnotations.iterator().next();}/*** 获取注解* 若有多个则只会返回第一个注解(方法上的优先于类上的)** @param element* @param annotationType* @param <A>* @return*/public static <A extends Annotation> A getAnnotation(AnnotatedElement element, Class<A> annotationType, AnnotationSearchConfig config) {Set<A> allAnnotations = getAllAnnotations(element, annotationType, config);return CollectionUtil.isEmpty(allAnnotations) ? null : allAnnotations.iterator().next();}/*** 获取方法和类上的注解** @param element* @return*/public static <A extends Annotation> Set<A> getAllAnnotations(AnnotatedElement element, Class<A> annotationType) {return getAllAnnotations(element, annotationType, DEFAULT_SEARCH_CONFIG);}/*** 获取方法和类上的注解** @param element* @return*/public static <A extends Annotation> Set<A> getAllAnnotations(AnnotatedElement element, Class<A> annotationType, AnnotationSearchConfig config) {if (config == null) {throw new NullPointerException("config can not be null");}if (config.isEnableCache()) {return (Set<A>) CACHE.computeIfAbsent(getCacheKey(element, annotationType, config), k -> getAllAnnotations(element, annotationType, config.getSearchSuperMaxDepth(), config));}return getAllAnnotations(element, annotationType, config.getSearchSuperMaxDepth(), config);}/*** 获取方法和类上的注解** @param element* @return*/private static <A extends Annotation> Set<A> getAllAnnotations(AnnotatedElement element, Class<A> annotationType, int depth, AnnotationSearchConfig config) {if (element == null || annotationType == null || depth <= 0 || config == null) {return Collections.emptySet();}boolean isClass = element instanceof Class;Class<?> declaringClass = isClass ? (Class<?>) element : element.getClass();if (element instanceof Executable) {declaringClass = ((Executable) element).getDeclaringClass();}if (element instanceof Member) {declaringClass = ((Member) element).getDeclaringClass();}if (element instanceof Parameter) {declaringClass = ((Parameter) element).getDeclaringExecutable().getDeclaringClass();}Class<?>[] interfaces = declaringClass.getInterfaces();Class<?> superclass = declaringClass.getSuperclass();// 自身的注解Set<A> methodAnnotationSet = isClass ? Collections.emptySet() : getAllMergedAnnotations(element, annotationType);// 类上的注解Set<A> classAnnotationSet = config.isSearchFromClass() ? getAllMergedAnnotations(declaringClass, annotationType) : Collections.emptySet();// 接口上的注解Set<A> interfaceAnnotationSet = config.isSearchFromInterfaces() ? (interfaces.length > 0 ? Arrays.stream(interfaces).map(interfaceCls -> getAllMergedAnnotations(interfaceCls, annotationType)).flatMap(Set::stream).collect(Collectors.toSet()) : Collections.emptySet()) : Collections.emptySet();// 父类上的注解Set<A> superClassAnnotationSet = config.isSearchSuperClass() ? (!Object.class.equals(superclass) ? getAllAnnotations(superclass, annotationType, depth - 1, config) : Collections.emptySet()) : Collections.emptySet();return Stream.of(methodAnnotationSet, classAnnotationSet, interfaceAnnotationSet, superClassAnnotationSet).flatMap(Set::stream).collect(Collectors.toSet());}
}

测试

定义了一个类,检查登录类型,用于限制某些端的用户只能访问某些端自己的接口
在这里插入图片描述
额外定义了多个快捷注解,快捷注解上标注了@CheckLoginType,预设了值。
在这里插入图片描述
在这里插入图片描述

方法上加注解,类上不加

在这里插入图片描述
调试结果
在这里插入图片描述

类上加注解、方法上加注解

在这里插入图片描述
运行调试
在这里插入图片描述
都能正常获取到方法、类上该注解(包括继承来的)

注解原理

注解基类Annotation,注释里写了从1.5版本引入的,所有的注解都会继承这个类,且不需要在定义的时候标明继承自这个类,只提供了annotationType来返回这个注解的Class,以及每个对象都有的equals、hashcode、toString()方法,那我们平时经常用的getAnnotation、isAnnotationPresent这些方法是哪里定义的呢?
在这里插入图片描述
我们通过方法找到接口为AnnotationElement,可以看到我们平时反射获取注解时的方法是定义在这个接口里的。
在这里插入图片描述

在这里插入图片描述
我们再看这个接口有哪些实现类,可以看到我们平时常用的类、方法、参数、构造方法等都实现了这个接口,因此这些地方都是可以加注解并通过反射拿到加了的注解的。

在这里插入图片描述
那我们看下在方法上获取注解时底层是怎么实现的,打开Method对象,可以看到它是调了父类的实现,继续往父类看
在这里插入图片描述
父类是Executable类
在这里插入图片描述
这个类是一个抽象类,上门写了是提供一些Method类和Constructor类公共使用的方法,它是一个共享的父类。
在这里插入图片描述
declaredAnnotations()的实现是,当前类有声明注解则直接返回当前类的declaredAnnotations这个Map,而这个map是记录的当前这个Method对象定义的一些注解,若有则直接返回这个,没有的话就会看有没有root,有root则返回root声明的所有注解的map。
在这里插入图片描述
而这个root对象,只有在执行Method.copy的时候才会有,那你可以当做root一直是个null的
在这里插入图片描述
因此平时你利用反射获取method的注解时其实就只获取到了这个方法上直接声明的注解,类上的,当前方法注解里继承的注解都不会返回的。这就是JDK方式的实现,因此spring在实现的时候提供了工具类去解决这个问题

性能测试

执行10000次,总耗时:240ms,平均耗时:0.024ms

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

音视频开发_SDL多线程

今天将向大家介绍一下SDL中的多线程的使用。通过下面对SDL 线程与锁相关的API介绍&#xff0c;你会发现&#xff0c;它与 Linux, Windows相关的API几乎是一模一样的。从这里可以推断出&#xff0c;其实SDL对于多线程的处理只是为大家提供了一套统一接口&#xff0c;并没有做其它…

汉化版PSAI全面测评,探索国产AI绘画软件的创新力量

引言 随着AI技术的飞速发展&#xff0c;图像处理和绘画领域迎来了新的变革。作为一名AIGC测评博主&#xff0c;今天我们测评的是一款国产AI绘画软件——StartAI&#xff0c;一句话总结&#xff1a;它不仅在技术上毫不逊色于国际大牌&#xff0c;更在用户体验和本地化服务上做到…

Python sorted()方法

sorted() 是Python中的一个内置函数&#xff0c;用于对可迭代对象进行排序。它返回一个新的已排序的列表&#xff0c;而不会修改原始的可迭代对象。sorted() 函数的基本语法如下&#xff1a; sorted(iterable, keyNone, reverseFalse)参数解释&#xff1a; iterable&#xff…

AI 克隆声音,只需 3 分钟(附最全教程)

作者&#xff1a;寒斜 继生成式文本大模型 Chatgpt&#xff0c;生成式图片 Stablediffusion 之后生成式语音 Text To Speech 在开源社区也出现了一匹黑马&#xff0c;就是 GPT-Sovits [ 1] 。 之所以说他是黑马&#xff0c;让人觉得惊艳&#xff0c;是因为在语音效果克隆上做…

github安全问题token和sshkeys

文章目录 sshkeys问题问题方法一:方法二:获取密钥添加密钥token问题问题:生成tokens设置tokenssshkeys问题 问题 当我们git clone代码时,会报如下错误,此时有2种解决方法。 git clone git@github.com:gjianw217/xboard-uboot.git Cloning into xboard-uboot... Permissio…

数据分析必备:一步步教你如何用matplotlib做数据可视化(6)

1、Matplotlib 网格 axes对象的grid()函数将图中网格的可见性设置为on或off。还可以显示网格的主要/次要(或两者)刻度。另外&#xff0c;可以在grid()函数中设置color&#xff0c;linestyle和linewidth属性。 参考以下示例代码 import matplotlib.pyplot as plt import numpy…

14K屏FPGA通过MIPI接口点亮

一、屏参数 屏分辨率为13320*5120&#xff0c;MIPI接口8 LANE。 二、驱动接口电路 屏偏置电压5.5V&#xff0c;逻辑供电1.8V。8 LANE MIPI&#xff0c;2 PORT。 三、MIPI DSI规范 DCS (Display Command Set)&#xff1a;DCS是一个标准化的命令集&#xff0c;用于命令模式的显…

微信公众号开发要点笔记

为了识别用户&#xff0c;每个用户针对每个公众号会产生一个安全的OpenID&#xff0c;如果需要在多公众号、移动应用之间做用户共通&#xff0c;则需前往微信开放平台&#xff0c;将这些公众号和应用绑定到一个开放平台账号下&#xff0c;绑定后&#xff0c;一个用户虽然对多个…

整理好了!2024年最常见 20 道设计模式面试题(五)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常见 20 道设计模式面试题&#xff08;四&#xff09;-CSDN博客 九、什么是命令模式&#xff1f;它如何帮助实现解耦&#xff1f; 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将一…

奇怪的bug

奇怪的bug 合集 1.不可见字符集问题 起因是在自己做小项目的时候&#xff0c;通过lombok的data注解&#xff0c;默认生成实体类的get set方法 但是在某个方法中获取一个属性值的时候显示找不到该属性值的get方法&#xff0c;具体直接贴图 我以为是lombok的配置问题&#xff0c…

ubuntu设置静态ip地址

首先&#xff0c;查看ip地址可以使用&#xff1a; ifconfig 例如&#xff0c;出现如下结果&#xff1a; 然后&#xff0c;需要查看本地的gateway以及dns&#xff0c;可以使用&#xff1a; nmcli device show例如&#xff1a; 接下来说明一下如何设置静态ip&#xff1a;

揭秘后勤报修管理系统:目的明确,功能设计模块助您轻松管理

在数字化、信息化飞速发展的今天&#xff0c;企业后勤报修也迎来了前所未有的变革。传统的报修方式&#xff0c;如电话报修、纸质报修单等&#xff0c;已逐渐无法满足现代企事业单位对高效、便捷、精准报修服务的需求。因此&#xff0c;后勤报修管理系统应运而生&#xff0c;后…

鸣人的影分身(DP)

在火影忍者的世界里&#xff0c;令敌人捉摸不透是非常关键的。 我们的主角漩涡鸣人所拥有的一个招数——多重影分身之术——就是一个很好的例子。 影分身是由鸣人身体的查克拉能量制造的&#xff0c;使用的查克拉越多&#xff0c;制造出的影分身越强。 针对不同的作战情况&a…

C#友元函数与友元类

在C#中&#xff0c;并没有像C那样的友元函数和友元类的直接语法支持。C#中的访问控制是通过public、private、protected等访问修饰符来实现的&#xff0c;而不是通过友元的概念。 友元函数与友元类的概念 在C中&#xff0c;友元函数和友元类允许一个类或者函数访问另一个类的…

PHP转Go系列 | 变量常量的使用姿势

大家好&#xff0c;我是码农先森。 变量 在 PHP 语言中&#xff0c;初始化变量虽然只有一行&#xff0c;其实包含了两步&#xff0c;一是声明变量&#xff0c;二是赋值给变量&#xff0c;同一个变量可以任意再赋值任何类型的数据。 <?php// 初始化变量 $name "man…

Z世代职场价值观的重塑:从“班味”心态到个人成长的追求

近日&#xff0c;社交平台Soul APP联合上海市精神卫生中心&#xff08;俗称“宛平南路600号”&#xff09;发布《2024年Z世代职场心理健康报告》&#xff08;下称“报告”&#xff09;&#xff0c;发现今天的年轻人正以其独特的价值观和行为模式&#xff0c;重新定义成功与成就…

java 带有返回值的异步处理

1、先创建一个线程池 // 创建一个固定大小的线程池ExecutorService executor Executors.newFixedThreadPool(5); 2、创建一个异步现成执行你所需要执行的业务 CompletableFuture<String> future CompletableFuture.supplyAsync(() -> {System.out.println(Thread.c…

118.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-邮件管理的界面与功能设计

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

visual studio error MSB8008:

新项目编译的时候&#xff0c;可能由于编译器的版本不一致导致的问题。 你的电脑上有两个不同版本的VS&#xff0c;或者你的程序拷贝到别人的电脑上去运行&#xff0c;或者你是从别人那里拷贝来的项目&#xff0c;而你们俩用的VS版本不一样&#xff0c;就会在运行的时候出现这…

初识Redis数据库(通俗易懂版)

Redis&#xff1a;不只是存储那么简单 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源且高性能的键值&#xff08;key-value&#xff09;存储系统&#xff0c;同时也是内存数据库&#xff0c;常用于缓存、消息队列、会话存储等应用场景。支持字符串&#x…