Java获取类方法上的注解
Java 获取类上的注解有下面 3 个方法:
- Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
- Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
- Class.getDeclaredAnnotations() 获取自己声明的注解
- 注解只有标注了 @Inherited 才能被子类继承
- 当某个类没有标注任何注解时,getAnnotations()和 getDeclaredAnnotations() 返回空数组
- 当某个注解查询不到时,getAnnotation(Class< A > annotationType) 方法返回 null
- 子类重写的方法,注解无法被继承,针对方法而言,getAnnotations()与 getDeclaredAnnotations() 返回的结果似乎永远都是一样的。
正常情况下,我们的 class 是 com
.cxytiandi.eureka_client.controller.ArticleController这种形式,如果用了 AOP 后,那么就会变成 com
.cxytiandi.eureka_client.controller.ArticleController$$EnhancerBySpringCGLIB$$3323dd1e这样了。
解决方案一
这种情况下拿到的 Method 也是被代理了的,所以 Method 上的注解自然获取不到,既然知道原因了,最简单快速的解决方法就是将多余的内容截取掉,然后重新得到一个没有被代理的 Class 对象,通过这个 Class 对象来获取 Method,这样就可以获取到 Method 上的注解。
Class
<?>
clz
=
bean
.
getClass
();
String
fullName
=
clz
.
getName
();
if
(
fullName
.
contains
(
"EnhancerBySpringCGLIB"
)
||
fullName
.
contains
(
"$$"
))
{
fullName
=
fullName
.
substring
(
0
,
fullName
.
indexOf
(
"$$"
));
try
{
clz
=
Class
.
forName
(
fullName
);
}
catch
(
ClassNotFoundException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
Method
[]
methods
=
clz
.
getMethods
();
for
(
Method
method
:
methods
)
{
if
(
method
.
isAnnotationPresent
(
Encrypt
.
class
))
{
String
uri
=
method
.
getAnnotation
(
Encrypt
.
class
).
value
();
}
}
解决方案二
虽然问题解决了,但是还是觉得不够优雅,有没有更好的方式呢?我们可以用 Spring 里面提供的 AnnotationUtils 来读取注解。
Encrypt
encrypt
=
AnnotationUtils
.
findAnnotation
(
method
,
Encrypt
.
class
);
if
(
encrypt
!=
null
)
{
String
uri
=
encrypt
.
value
();
}
AnnotationUtils.findAnnotation() 原理是什么呢?为什么它可以获取到被代理后方法上的注解呢?
要想知道原理,那就只能看源码啦,源码多,不贴出来了,贴一点点关键的就行了
首先会构建一个 AnnotationCacheKey,从本地缓存中获取,如果有的话直接返回,也就意味着只要读取过就会被缓存起来:
AnnotationCacheKey
cacheKey
=
new
AnnotationCacheKey
(
method
,
annotationType
);
A result
=
(
A
)
findAnnotationCache
.
get
(
cacheKey
);
然后就是判断是否桥接方法,如果不是就直接返回,是的话则获取桥接方法的注解,如果还获取不到就通过接口来获取。
Method
resolvedMethod
=
BridgeMethodResolver
.
findBridgedMethod
(
method
);
result
=
findAnnotation
((
AnnotatedElement
)
resolvedMethod
,
annotationType
);
if
(
result
==
null
)
{
result
=
searchOnInterfaces
(
method
,
annotationType
,
method
.
getDeclaringClass
().
getInterfaces
());}
后面就不继续下去了,最关键的代码其实是这句:
clazz = clazz.getSuperclass();
因为 CGLIB 代理会为目标类动态生成一个子类,所以我们要获取最原始的类,直接使用 getSuperclass 就可以了,跟第一种方案是一致的,只是第一种看起来有点那啥哈.....
推荐大家用 AnnotationUtils 去获取,这里面封装了很多的逻辑,考虑了很多场景下的问题,切莫重复造轮子。