Java获取类上的注解有下面3个方法:

  • Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
  • Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
  • Class.getDeclaredAnnotations() 获取自己声明的注解
  1. 注解只有标注了@Inherited才能被子类继承
  2. 当某个类没有标注任何注解时,getAnnotations()和getDeclaredAnnotations()返回空数组
  3. 当某个注解查询不到时,getAnnotation(Class< A > annotationType)方法返回null
  4. 子类重写的方法,注解无法被继承,针对方法而言,getAnnotations()与getDeclaredAnnotations()返回的结果似乎永远都是一样的。
如果类被代理,如何获取到类的注解呢?

正常情况下,我们的class是 com.cxytiandi.eureka_client.controller.ArticleController这种形式,如果用了AOP后,那么就会变成 com.cxytiandi.eureka_client.controller.ArticleController$$EnhancerBySpringCGLIB$$3323dd1e这样了。

解决方案一

这种情况下拿到的Method也是被代理了的,所以Method上的注解自然获取不到,既然知道原因了,最简单快速的解决方法就是将多余的内容截取掉,然后重新得到一个没有被代理的Class对象,通过这个Class对象来获取Method,这样就可以获取到Method上的注解。

  1. Class<?> clz = bean.getClass();
  2. String fullName = clz.getName();
  3. if (fullName.contains("EnhancerBySpringCGLIB") || fullName.contains("$$")) {
  4. fullName = fullName.substring(0, fullName.indexOf("$$"));
  5. try {
  6. clz = Class.forName(fullName);
  7. } catch (ClassNotFoundException e) {
  8. throw new RuntimeException(e);
  9. }
  10. }
  11. Method[] methods = clz.getMethods();
  12. for (Method method : methods) {
  13. if (method.isAnnotationPresent(Encrypt.class)) {
  14. String uri = method.getAnnotation(Encrypt.class).value();
  15. }
  16. }

解决方案二

虽然问题解决了,但是还是觉得不够优雅,有没有更好的方式呢?我们可以用Spring里面提供的AnnotationUtils来读取注解。

  1. Encrypt encrypt = AnnotationUtils.findAnnotation(method, Encrypt.class);
  2. if (encrypt != null) {
  3. String uri = encrypt.value();
  4. }

AnnotationUtils.findAnnotation()原理是什么呢?为什么它可以获取到被代理后方法上的注解呢?

要想知道原理,那就只能看源码啦,源码多,不贴出来了,贴一点点关键的就行了

首先会构建一个AnnotationCacheKey,从本地缓存中获取,如果有的话直接返回,也就意味着只要读取过就会被缓存起来:

  1. AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
  2. A result = (A) findAnnotationCache.get(cacheKey);

然后就是判断是否桥接方法,如果不是就直接返回,是的话则获取桥接方法的注解,如果还获取不到就通过接口来获取。

  1. Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
  2. result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
  3. if (result == null) {
  4. result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
  5. }

后面就不继续下去了,最关键的代码其实是这句:

  1. clazz = clazz.getSuperclass();

因为CGLIB代理会为目标类动态生成一个子类,所以我们要获取最原始的类,直接使用getSuperclass就可以了,跟第一种方案是一致的,只是第一种看起来有点那啥哈.....

推荐大家用AnnotationUtils去获取,这里面封装了很多的逻辑,考虑了很多场景下的问题,切莫重复造轮子。

Java获取类方法上的注解的更多相关文章

  1. AOP方法拦截获取参数上的注解

    https://www.jianshu.com/p/f5c7417a75f9 获取参数注解 在spring aop中,无论是前置通知的参数JoinPoint,还是环绕通知的参数ProceedingJo ...

  2. Java获取Linux上指定文件夹下所有第一级子文件夹

    说明:需要只获得第一级文件夹目录 package com.sunsheen.jfids.studio.monitor.utils; import java.io.BufferedReader; imp ...

  3. java获取当前上一周、上一月、上一年的时间

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar c = Calend ...

  4. 黑马程序员——【Java高新技术】——JavaBean、注解

    ---------- android培训.java培训.期待与您交流! ---------- 一.JavaBean * 通过内省引入JavaBean:内省对应的英文全程是IntroSpector.在J ...

  5. 夯实Java基础(十七)——注解(Annotation)

    1.注解概述 从JDK5.0开始,Java增加对元数据(MetaData)的支持,也就是注解(Annotation).其实我们早就已经接触过注解了,例如我们经常在Java代码中可以看到 “@Overr ...

  6. java反射之获取所有方法及其注解(包括实现的接口上的注解),获取各种标识符备忘

    java反射之获取类或接口上的所有方法及其注解(包括实现的接口上的注解) /** * 获取类或接口上的所有方法及方法上的注解(包括方法实现上的注解以及接口上的注解),最完整的工具类,没有现成的工具类 ...

  7. spring aop获取目标对象的方法对象(包括方法上的注解)

    这两天在学习权限控制模块.以前看过传智播客黎活明老师的巴巴运动网视频教程,里面就讲到权限控制的解决方案,当时也只是看看视频,没有动手实践,虽说看过几遍,可是对于系统中的权限控制还是很迷茫,所以借着这次 ...

  8. Spring:使用Spring AOP时,如何获取目标方法上的注解

    当使用spring AOP时,判断目标方法上的注解进行相关操作,如缓存,认证权限等 自定义注解 package com.agent.annotation; import java.lang.annot ...

  9. linux下的shell命令的编写,以及java怎样调用linux的shell命令(java怎样获取linux上的网卡的ip信息)

    程序猿都非常懒,你懂的! 近期在开发中,须要用到server的ip和mac信息.可是server是架设在linux系统上的,对于多网口,在获取ip时就产生了非常大的问题.以下是在windows系统上, ...

随机推荐

  1. javascript匿名函数自执行 (function(window,document,undefined){})(window,document);

    使用匿名自执行函数的作用: (function(window,document,undefined){})(window,document); 1.首先匿名函数 (function(){}) (); ...

  2. COGS 有标号的DAG/强连通图计数

    COGS索引 一堆神仙容斥+多项式-- 有标号的DAG计数 I 考虑\(O(n^2)\)做法:设\(f_i\)表示总共有\(i\)个点的DAG数量,转移考虑枚举DAG上所有出度为\(0\)的点,剩下的 ...

  3. shell-基础2-字符串文本处理${}

    一.为什么使用${}引用变量 1.$a和${a}的效果与区别 因为个别特殊字符会影响正常引用,所以需要使用${}引用变量,加花括号是为了帮助解释器识别变量的边界 $a和${a}效果一样,当变量后面连接 ...

  4. 使用交叉验证法(Cross Validation)进行模型评估

    scikit-learn中默认使用的交叉验证法是K折叠交叉验证法(K-fold cross validation):它将数据集拆分成k个部分,再用k个数据集对模型进行训练和评分. 1.K折叠交叉验证法 ...

  5. Ant Design Pro实现导出Excel

    react Ant Design ProUI框架导出Excel(只能导出当前列表数据) 插件安装 npm install js-export-excel 安装完成之后开始引入 import Expor ...

  6. Javap与JVM指令

    一.javap命令简述 javap是jdk自带的反解析工具.它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令).本地变量表.异常表和代码行偏移量映射表.常量池等等信息.当 ...

  7. jenkins安装和简单部署

    jenkins安装和简单部署 jenkins历史 jenkins是一款非常好用的团队CI(Continuous Integration)工具.它可以使你的构建,集成,发布,开发流程自动化.减轻各个环节 ...

  8. kubernetes使用Traefik暴露web服务-转载51cto

    Traefix介绍(摘自网络) traefik 是一个前端负载均衡器,对于微服务架构尤其是 kubernetes 等编排工具具有良好的支持:同 nginx 等相比,traefik 能够自动感知后端容器 ...

  9. 通过visual studio制作类库的文档

    java的集成开发工具,可以导出jar的文档. visual studio 也可以生成类库的文档,邮件项目属性,生成,输出下,选择XML文档文件.然后生成项目,就会再bin下面生成一个xml文件. 将 ...

  10. 国际化(i18n) 各国语言缩写

    internationalization (国际化)简称:i18n,因为在i和n之间还有18个字符,localization(本地化 ),简称L10n. 一般用语言_地区的形式表示一种语言,如:zh_ ...