菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
404
0

Java获取类方法上的注解

原创
05/13 14:22
阅读数 63830

 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去获取,这里面封装了很多的逻辑,考虑了很多场景下的问题,切莫重复造轮子。

发表评论

0/200
404 点赞
0 评论
收藏
为你推荐 换一批