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

news2024/11/23 22:30:36

文章目录

  • 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
    @ToString
    public 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.coloradmin.cn/o/1841017.html

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

相关文章

技术差异,应用场景;虚拟机可以当作云服务器吗

虚拟机和云服务器是现在市面上常见的两种计算资源提供方式&#xff0c;很多人把这两者看成可以相互转换或者替代的物品&#xff0c;实则不然&#xff0c;这两种资源提供方式有许多相似之处&#xff0c;但是也有不少区别&#xff0c;一篇文章教你识别两者的技术差异&#xff0c;…

人工智能中的监督学习和无监督学习

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f349;监督学习 &#x1f348;基本思想 &#x1f348;具体过程 &#x1f34d;数据收集 &#x1f34d;数据预处理 &#x1f34d;模型选择 &#x1f34d;模型训练 &#x1f34d;模型评估 &#x1f34d;模型部署…

深入理解预处理

1.预定义符号 C语言设置了⼀些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C&…

DSP28335:中断系统

1.中断介绍 F28335的中断&#xff1a; 中断源可分为片内中断源和片外中断源&#xff0c;这些外设中断源需要把中断请求传递给内核需要中断线。 14个课屏蔽中断是通过相应的使能寄存器控制是否进行中断。 28335总共的中断源有58个&#xff0c;可是只有12个中断线&#xff0c;如…

分页插件bs_pagination与Bootstrap4、jQuery3.7.1不兼容的问题

问题场景&#xff1a; 开发环境&#xff1a; 项目&#xff1a;CRM IDE&#xff1a;intelij IDEA JDK: jdk21.0.2 JQuery&#xff1a;3.7.1 Bootstrap&#xff1a;4.6 项目中需要查询数据库中的数据&#xff0c;并且以分页显示的方式显示在页面上&#xff0c;前端页面用到了分页…

嵌入式实验---实验二 中断功能实验

一、实验目的 1、掌握STM32F103中断程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、在上一章的实验基础上&#xff0c;添加一个按键和一个LED&#xff1b; 2、使用中断的方式实现以下两个功能&#xff1a; &#xff08;1&#xff09;KEY1按键…

考研计组chap4指令系统

目录 一、指令格式 155 13.操作码地址码 2.按照地址码数量 &#xff08;1&#xff09;零地址指令 &#xff08;2&#xff09;一地址指令 &#xff08;3&#xff09;二地址指令 &#xff08;4&#xff09;三地址指令 &#xff08;5&#xff09;四地址指令 3.指令长度 …

RabbitMQ实践——超时消息的处理方法

大纲 准备工作整个队列的消息都有相同的时效性抛弃超时消息新建带x-message-ttl的队列新建绑定关系实验 超时消息路由到死信队列新建带死信和ttl的队列新建绑定关系实验 消息指定自己的超时时间新建带死信的队列绑定实验 消息自带TTL和队列TTL的关系消息TTL < 队列指定TTL消…

vxe-table 列表过滤踩坑_vxe-table筛选

但是这个过滤输入值必须是跟列表的值必须一致才能查到&#xff0c;没做到模糊查询的功能&#xff0c;根据关键字来过滤并没有实现。 下面提供一下具体实现方法&#xff1a;&#xff08;关键字来过滤&#xff09; filterNameMethod({ option, row }) {if (row.name.indexOf(op…

Vue65-vue-resource:ajax请求

vue-resource是vue的插件库&#xff0c;用vue.use(xxxx)使用插件。 1、安装 2、引入和使用 这个库&#xff0c;维护的频率不高了。还是建议使用&#xff1a;axios&#xff0c;vue-resource只是了解即可。

Qt Quick 教程(一)

文章目录 1.Qt Quick2.QML3.Day01 案例main.qml退出按钮&#xff0c;基于上面代码添加 4.使用Qt Design StudioQt Design Studio简介Qt Design Studio工具使用版本信息 1.Qt Quick Qt Quick 是一种现代的用户界面技术&#xff0c;将声明性用户界面设计和命令性编程逻辑分开。 …

win11照片裁剪视频无法保存问题解决

win11照片默认走核显&#xff0c;intel的显卡可能无法解码&#xff0c;在设置里把照片的显示卡默认换成显卡就好了

基于百度飞桨PaddleOCR应用开发实践银行卡卡面内容检测识别系统

OCR相关的内容我在之前的工作中虽有所涉及&#xff0c;但是还是比较少的&#xff0c;最近正好需要用到OCR的一些技术&#xff0c;查了一些资料&#xff0c;发现国内的话百度这块做的还是比较全面系统深入的&#xff0c;抱着闲来无事学习了解的心态&#xff0c;这里花了点时间基…

M1失效后,哪个是观察A股的关键新指标?

M1失效后&#xff0c;哪个是观察A股的关键新指标&#xff1f; 央地支出增速差&#xff08;地方-中央支出增速的差值&#xff09;或许是解释沪深300定价更有效的前瞻指标。该数值扩张&#xff0c;则有利于大盘指数&#xff0c;反之亦然&#xff0c;该指标从2017年至今对大盘指数…

【CSS in Depth2精译】1.1.4 源码顺序

解决层叠冲突的最后一环叫做 源码顺序&#xff0c;有时又称为 出现顺序&#xff08;order of appearance&#xff09;。如果其他判定规则均一致&#xff0c;则样式表中后出现的、或者在页面较晚引入的样式表声明&#xff0c;将最终胜出。 也就是说&#xff0c;可以通过控制源码…

解决WebStorm中不显示npm任务面板

鼠标右键项目的package.json文件&#xff0c;然后点击show npm scripts选项。 然后npm工具窗口就显示了&#xff1a;

基于`肿瘤浸润淋巴细胞`的AI风险评分预测`鼻咽癌`的`无局部生存率`|顶刊速递·24-06-20

小罗碎碎念 本期分享的文献是“AI-Based Risk Score from Tumour-Infiltrating Lymphocyte Predicts Locoregional-Free Survival in Nasopharyngeal Carcinoma”&#xff0c;翻译一下&#xff0c;即—— 基于肿瘤浸润淋巴细胞的人工智能风险评分预测鼻咽癌的无局部生存率。 文…

计算机网络——网络层重要协议(IP)

网络层的作用&#xff1a;在复杂的网络环境中确定一个合适的路径 IP 协议&#xff08;Internet Protocol&#xff09; IP 数据报格式 4 位版本号&#xff1a;指定 IP 协议的版本&#xff0c;对于 IPV4 来说就是 44 位首部长度&#xff1a;用于表示 IP 首部的长度&#xff0c…

Java零基础之多线程篇:线程生命周期

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

数据库 | SQL语言和关系代数汇总篇(*^▽^*)

双重/两个 not exists 嵌套查询 简单分析_两个not exists-CSDN博客 最明白的 not exists 双层嵌套讲解 SQL 查询语句_not exist 嵌套-CSDN博客 1. 答案&#xff1a; 2. 答案&#xff1a; 3. 4. 5. 6. 7. 8. 这个SQL查询是从两个表&#xff08;假设是SPJ和P&#xff09;中检…