【Java】關於獲取註解的問題發現
阿新 • • 發佈:2021-10-26
同事設定了個註解,想用Spring獲取的Bean來找到Class獲取註解
但是發現是空的,在檢視的Spring返回Bean之後,發現這個Bean物件並不是原生的例項
而是被Spring代理增強的代理物件
為了復現這個問題,這裡我寫了個樣例:
首先有兩個樣例註解(一個叫A一個叫B,B也是一樣的就不寫了):
package cn.cloud9.test.reflect.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AnnoA { String value() default ""; }
再寫一個A類,裡面就是一個方法沒別的
打上A和B註解
package cn.cloud9.test.reflect.sample; import cn.cloud9.test.reflect.annotation.AnnoA; import cn.cloud9.test.reflect.annotation.AnnoB; @AnnoA @AnnoBpublic class A { public void fun() { System.out.println("A.fun executed... "); } }
為了模擬Spring代理的場景,這裡要引入CGLIB元件
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
編寫樣例程式碼:
package cn.cloud9.test.reflect; import cn.cloud9.test.reflect.sample.A; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class ReflectAndAnnotation { @Test public void simpleGet() throws Exception { Class<?> aClass = Class.forName("cn.cloud9.test.reflect.sample.A"); Annotation[] annotations = aClass.getAnnotations(); Class<?> superclass = aClass.getSuperclass(); System.out.println(superclass); for (Annotation annotation : annotations) { System.out.println(annotation.toString()); } } }
執行之後,自然而然的就能得到我們設定的註解:
"C:\Program Files\Java\jdk1.8.0_301\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=57548:C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar;C:\Users\Cloud9\IdeaProjects\EveryThing-Framework\target\test-classes;C:\Users\Cloud9\IdeaProjects\EveryThing-Framework\target\classes;D:\Maven Local Repository\org\apache\curator\curator-framework\5.2.0\curator-framework-5.2.0.jar;D:\Maven Local Repository\org\apache\curator\curator-client\5.2.0\curator-client-5.2.0.jar;D:\Maven Local Repository\org\apache\zookeeper\zookeeper\3.6.3\zookeeper-3.6.3.jar;D:\Maven Local Repository\org\apache\zookeeper\zookeeper-jute\3.6.3\zookeeper-jute-3.6.3.jar;D:\Maven Local Repository\org\apache\yetus\audience-annotations\0.5.0\audience-annotations-0.5.0.jar;D:\Maven Local Repository\io\netty\netty-handler\4.1.63.Final\netty-handler-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-common\4.1.63.Final\netty-common-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-resolver\4.1.63.Final\netty-resolver-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-buffer\4.1.63.Final\netty-buffer-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport\4.1.63.Final\netty-transport-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-codec\4.1.63.Final\netty-codec-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport-native-epoll\4.1.63.Final\netty-transport-native-epoll-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport-native-unix-common\4.1.63.Final\netty-transport-native-unix-common-4.1.63.Final.jar;D:\Maven Local Repository\org\apache\curator\curator-recipes\5.2.0\curator-recipes-5.2.0.jar;D:\Maven Local Repository\commons-io\commons-io\2.11.0\commons-io-2.11.0.jar;D:\Maven Local Repository\commons-beanutils\commons-beanutils\1.7.0\commons-beanutils-1.7.0.jar;D:\Maven Local Repository\commons-logging\commons-logging\1.0.3\commons-logging-1.0.3.jar;D:\Maven Local Repository\org\javassist\javassist\3.26.0-GA\javassist-3.26.0-GA.jar;D:\Maven Local Repository\com\alibaba\fastjson\1.2.78\fastjson-1.2.78.jar;D:\Maven Local Repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;D:\Maven Local Repository\javax\servlet\jstl\1.2\jstl-1.2.jar;D:\Maven Local Repository\javax\servlet\jsp\javax.servlet.jsp-api\2.3.3\javax.servlet.jsp-api-2.3.3.jar;D:\Maven Local Repository\org\reflections\reflections\0.9.11\reflections-0.9.11.jar;D:\Maven Local Repository\com\google\guava\guava\20.0\guava-20.0.jar;D:\Maven Local Repository\mysql\mysql-connector-java\8.0.25\mysql-connector-java-8.0.25.jar;D:\Maven Local Repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;D:\Maven Local Repository\io\jsonwebtoken\jjwt\0.9.1\jjwt-0.9.1.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-databind\2.9.6\jackson-databind-2.9.6.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-core\2.9.6\jackson-core-2.9.6.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpcore\4.4.13\httpcore-4.4.13.jar;D:\Maven Local Repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpmime\4.5.13\httpmime-4.5.13.jar;D:\Maven Local Repository\junit\junit\4.12\junit-4.12.jar;D:\Maven Local Repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\Maven Local Repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Maven Local Repository\org\slf4j\slf4j-log4j12\1.7.32\slf4j-log4j12-1.7.32.jar;D:\Maven Local Repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Maven Local Repository\cglib\cglib\2.2.2\cglib-2.2.2.jar;D:\Maven Local Repository\asm\asm\3.3.1\asm-3.3.1.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 cn.cloud9.test.reflect.ReflectAndAnnotation,simpleGet class java.lang.Object @cn.cloud9.test.reflect.annotation.AnnoA(value=) @cn.cloud9.test.reflect.annotation.AnnoB(value=) Process finished with exit code 0
那麼在呼叫CGLIB的增強代理之後:
package cn.cloud9.test.reflect; import cn.cloud9.test.reflect.sample.A; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class ReflectAndAnnotation { @Test public void simpleGet() throws Exception { Class<?> aClass = Class.forName("cn.cloud9.test.reflect.sample.A"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(aClass); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method run..."); Object result = proxy.invokeSuper(obj, args); System.out.println("after method run..."); return result; } }); A proxyA = (A) enhancer.create(); proxyA.fun(); Class<? extends A> proxyAClass = proxyA.getClass(); System.out.println(proxyAClass); for (Annotation annotation : proxyAClass.getAnnotations()) { System.out.println(annotation); } } }
執行發現,我們註解獲取不到了
"C:\Program Files\Java\jdk1.8.0_301\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=57581:C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar;C:\Users\Cloud9\IdeaProjects\EveryThing-Framework\target\test-classes;C:\Users\Cloud9\IdeaProjects\EveryThing-Framework\target\classes;D:\Maven Local Repository\org\apache\curator\curator-framework\5.2.0\curator-framework-5.2.0.jar;D:\Maven Local Repository\org\apache\curator\curator-client\5.2.0\curator-client-5.2.0.jar;D:\Maven Local Repository\org\apache\zookeeper\zookeeper\3.6.3\zookeeper-3.6.3.jar;D:\Maven Local Repository\org\apache\zookeeper\zookeeper-jute\3.6.3\zookeeper-jute-3.6.3.jar;D:\Maven Local Repository\org\apache\yetus\audience-annotations\0.5.0\audience-annotations-0.5.0.jar;D:\Maven Local Repository\io\netty\netty-handler\4.1.63.Final\netty-handler-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-common\4.1.63.Final\netty-common-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-resolver\4.1.63.Final\netty-resolver-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-buffer\4.1.63.Final\netty-buffer-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport\4.1.63.Final\netty-transport-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-codec\4.1.63.Final\netty-codec-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport-native-epoll\4.1.63.Final\netty-transport-native-epoll-4.1.63.Final.jar;D:\Maven Local Repository\io\netty\netty-transport-native-unix-common\4.1.63.Final\netty-transport-native-unix-common-4.1.63.Final.jar;D:\Maven Local Repository\org\apache\curator\curator-recipes\5.2.0\curator-recipes-5.2.0.jar;D:\Maven Local Repository\commons-io\commons-io\2.11.0\commons-io-2.11.0.jar;D:\Maven Local Repository\commons-beanutils\commons-beanutils\1.7.0\commons-beanutils-1.7.0.jar;D:\Maven Local Repository\commons-logging\commons-logging\1.0.3\commons-logging-1.0.3.jar;D:\Maven Local Repository\org\javassist\javassist\3.26.0-GA\javassist-3.26.0-GA.jar;D:\Maven Local Repository\com\alibaba\fastjson\1.2.78\fastjson-1.2.78.jar;D:\Maven Local Repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;D:\Maven Local Repository\javax\servlet\jstl\1.2\jstl-1.2.jar;D:\Maven Local Repository\javax\servlet\jsp\javax.servlet.jsp-api\2.3.3\javax.servlet.jsp-api-2.3.3.jar;D:\Maven Local Repository\org\reflections\reflections\0.9.11\reflections-0.9.11.jar;D:\Maven Local Repository\com\google\guava\guava\20.0\guava-20.0.jar;D:\Maven Local Repository\mysql\mysql-connector-java\8.0.25\mysql-connector-java-8.0.25.jar;D:\Maven Local Repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;D:\Maven Local Repository\io\jsonwebtoken\jjwt\0.9.1\jjwt-0.9.1.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-databind\2.9.6\jackson-databind-2.9.6.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\Maven Local Repository\com\fasterxml\jackson\core\jackson-core\2.9.6\jackson-core-2.9.6.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpcore\4.4.13\httpcore-4.4.13.jar;D:\Maven Local Repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\Maven Local Repository\org\apache\httpcomponents\httpmime\4.5.13\httpmime-4.5.13.jar;D:\Maven Local Repository\junit\junit\4.12\junit-4.12.jar;D:\Maven Local Repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\Maven Local Repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Maven Local Repository\org\slf4j\slf4j-log4j12\1.7.32\slf4j-log4j12-1.7.32.jar;D:\Maven Local Repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Maven Local Repository\cglib\cglib\2.2.2\cglib-2.2.2.jar;D:\Maven Local Repository\asm\asm\3.3.1\asm-3.3.1.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 cn.cloud9.test.reflect.ReflectAndAnnotation,simpleGet before method run... A.fun executed... after method run... class cn.cloud9.test.reflect.sample.A$$EnhancerByCGLIB$$fe34985a Process finished with exit code 0
為什麼獲取不到?
因為這個是代理的物件,那麼代理物件也應該由一個類來建立
這個類是代理物件類 class cn.cloud9.test.reflect.sample.A$$EnhancerByCGLIB$$fe34985a,那麼代理物件類會有我們設定的註解嗎?
結果是沒有的,但是根據CGLIB代理的實現原理,CGLIB代理原理是通過繼承實現的,所以我們是不是可以通過代理類向上找父類來獲取?
下面就是對猜想的測試:
package cn.cloud9.test.reflect; import cn.cloud9.test.reflect.sample.A; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class ReflectAndAnnotation { @Test public void simpleGet() throws Exception { Class<?> aClass = Class.forName("cn.cloud9.test.reflect.sample.A"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(aClass); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method run..."); Object result = proxy.invokeSuper(obj, args); System.out.println("after method run..."); return result; } }); A proxyA = (A) enhancer.create(); proxyA.fun(); Class<? extends A> proxyAClass = proxyA.getClass(); System.out.println(proxyAClass); for (Annotation annotation : proxyAClass.getAnnotations()) { System.out.println(annotation); } // 獲取父類 Class<?> superclass1 = proxyAClass.getSuperclass(); System.out.println(superclass1); for (Annotation annotation : superclass1.getAnnotations()) { System.out.println(annotation); } } }
執行結果發現,代理物件的父類就是我們原來的類
從原類再次獲取註解,結果是可以的
before method run... A.fun executed... after method run... class cn.cloud9.test.reflect.sample.A$$EnhancerByCGLIB$$fe34985a class cn.cloud9.test.reflect.sample.A @cn.cloud9.test.reflect.annotation.AnnoA(value=) @cn.cloud9.test.reflect.annotation.AnnoB(value=)
由此可以推斷JDK的介面實現代理的方式
物件要增強就需要通過介面實現處理,也就是說實際的介面物件,是實現類的物件
註解在介面上,要通過實現類找到對應的介面,通過介面例項,再找到標註的註解
註解在實現類上,就可以直接通過實現類,獲取標註的註解