Java獲取類方法上的注解


 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去獲取,這里面封裝了很多的邏輯,考慮了很多場景下的問題,切莫重復造輪子。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM