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

本文主要是介绍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

在这里插入图片描述

在这里插入图片描述

这篇关于JAVA 注解搜索工具类与注解原理讲解(获取方法和类上所有的某个注解,父类继承的注解也支持获取)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1077453

相关文章

Spring Boot整合Redis注解实现增删改查功能(Redis注解使用)

《SpringBoot整合Redis注解实现增删改查功能(Redis注解使用)》文章介绍了如何使用SpringBoot整合Redis注解实现增删改查功能,包括配置、实体类、Repository、Se... 目录配置Redis连接定义实体类创建Repository接口增删改查操作示例插入数据查询数据删除数据更

Java Lettuce 客户端入门到生产的实现步骤

《JavaLettuce客户端入门到生产的实现步骤》本文主要介绍了JavaLettuce客户端入门到生产的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录1 安装依赖MavenGradle2 最小化连接示例3 核心特性速览4 生产环境配置建议5 常见问题

使用python生成固定格式序号的方法详解

《使用python生成固定格式序号的方法详解》这篇文章主要为大家详细介绍了如何使用python生成固定格式序号,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录生成结果验证完整生成代码扩展说明1. 保存到文本文件2. 转换为jsON格式3. 处理特殊序号格式(如带圈数字)4

Java使用Swing生成一个最大公约数计算器

《Java使用Swing生成一个最大公约数计算器》这篇文章主要为大家详细介绍了Java使用Swing生成一个最大公约数计算器的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下... 目录第一步:利用欧几里得算法计算最大公约数欧几里得算法的证明情形 1:b=0情形 2:b>0完成相关代码第二步:加

Java 的ArrayList集合底层实现与最佳实践

《Java的ArrayList集合底层实现与最佳实践》本文主要介绍了Java的ArrayList集合类的核心概念、底层实现、关键成员变量、初始化机制、容量演变、扩容机制、性能分析、核心方法源码解析、... 目录1. 核心概念与底层实现1.1 ArrayList 的本质1.1.1 底层数据结构JDK 1.7

Java Map排序如何按照值按照键排序

《JavaMap排序如何按照值按照键排序》该文章主要介绍Java中三种Map(HashMap、LinkedHashMap、TreeMap)的默认排序行为及实现按键排序和按值排序的方法,每种方法结合实... 目录一、先理清 3 种 Map 的默认排序行为二、按「键」排序的实现方式1. 方式 1:用 TreeM

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代