一般咱們想要在java運行時獲取class的信息時,一般使用反射的方式來獲取其中的屬性,方法,註解等信息。一般是這樣的:html
Class<Aoo> aooClass = Aoo.class; //獲取declaredMethod for (Method declaredMethod : aooClass.getDeclaredMethods()) { System.out.println("declaredMethod.getName() : " + declaredMethod.getName()); System.out.println("declaredMethod.getReturnType(): " + declaredMethod.getReturnType().getName()); } //獲取DeclaredField for (Field field : aooClass.getDeclaredFields()) { System.out.println("field.getName() : " + field.getName()); System.out.println("field.getType() : " + field.getType().getName()); } //獲取Annotation for (Annotation annotation : aooClass.getAnnotations()) { System.out.println("annotation.annotationType() : " + annotation.annotationType().getName()); } ... 獲取其餘的一些信息
雖然用起來也是很好用,api也不復雜,可是因爲使用反射對性能的開銷比較大,性能不是很好。咱們能夠經過asm來獲取class中的信息。java
官網:https://asm.ow2.io/web
ASM是一個通用的Java字節碼操做和分析框架。它能夠用於修改現有類或直接以二進制形式動態生成類。ASM提供了一些常見的字節碼轉換和分析算法,能夠從中構建自定義複雜轉換和代碼分析工具。ASM提供與其餘Java字節碼框架相似的功能,但專一於 性能。由於它的設計和實現儘量小並且快,因此它很是適合在動態系統中使用(但固然也能夠以靜態方式使用,例如在編譯器中)。算法
嗯~api
<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>7.1</version> </dependency>
如今的asm版本是7.1,因此這一內容都以7.1的版本爲主。框架
由於咱們要作的是獲取class中的各類信息,因此咱們須要用到下面一些對象:函數
可使用以下命令:工具
//命令 javap -v Aoo.class //結果 。。。省略。。。 public class com.hebaibai.example.demo.Demo minor version: 0 major version: 52 //這裏是訪問標示 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #22.#42 // java/lang/Object."<init>":()V #2 = Methodref #43.#44 // java/lang/System.currentTimeMillis:()J #3 = Class #45 // org/objectweb/asm/ClassReader 。。。省略。。。
訪問標示有這麼幾種:性能
名稱 | 值 |
---|---|
ACC_ABSTRACT | 1024 |
ACC_ANNOTATION | 8192 |
ACC_BRIDGE | 64 |
ACC_DEPRECATED | 131072 |
ACC_ENUM | 16384 |
ACC_FINAL | 16 |
ACC_INTERFACE | 512 |
ACC_MANDATED | 32768 |
ACC_MODULE | 32768 |
ACC_NATIVE | 256 |
ACC_OPEN | 32 |
ACC_PRIVATE | 2 |
ACC_PROTECTED | 4 |
ACC_PUBLIC | 1 |
ACC_STATIC | 8 |
ACC_STATIC_PHASE | 64 |
ACC_STRICT | 2048 |
ACC_SUPER | 32 |
ACC_SYNCHRONIZED | 32 |
ACC_SYNTHETIC | 4096 |
ACC_TRANSIENT | 128 |
ACC_TRANSITIVE | 32 |
ACC_VARARGS | 128 |
ACC_VOLATILE | 64 |
其中方法的返回值是上面幾個表格中幾個參數相加的結果。好比若是結果爲33,那麼就是ACC_PUBLIC與ACC_SUPER相加的結果,表明是一個public修飾的類。ssr
api版本不一樣支持的功能也不一樣,經過查看代碼,大體上有如下區別,可能有遺漏或者錯誤。高版本不支持的功能,低版本一樣不支持。
不支持:
//方法 ClassVisitor.visitTypeAnnotation FieldVisitor.visitTypeAnnotation MethodVisitor.visitTypeAnnotation MethodVisitor.visitParameter MethodVisitor.visitMethodInsn MethodVisitor.visitInvokeDynamicInsn MethodVisitor.visitLdcInsn MethodVisitor.visitInsnAnnotation MethodVisitor.visitTryCatchAnnotation MethodVisitor.visitLocalVariableAnnotation
不支持:
//方法 ClassVisitor.visitModule //對象 ModuleVisitor
不支持
//方法 ClassVisitor.visitNestHost ClassVisitor.visitNestMember MethodVisitor.visitLdcInsn
應該是沒有不支持的方法。
這裏只是介紹一些我感受經常使用的一些方法,要看詳細內容的話,請看官方的文檔:https://asm.ow2.io/javadoc/overview-summary.html
//使用class的名稱 ClassReader classReader = new ClassReader(Aoo.class.getName()); //使用InputStream File classFile = new File("/home/hjx/demo/target/classes/com/hebaibai/example/demo/Aoo.class"); ClassReader classReader = new ClassReader(new FileInputStream(classFile)); //使用byte[] File classFile = new File("/home/hjx/demo/target/classes/com/hebaibai/example/demo/Aoo.class"); FileInputStream inputStream = new FileInputStream(classFile); byte[] classBytes = new byte[inputStream.available()]; inputStream.read(classBytes); ClassReader classReader = new ClassReader(classBytes);
使用給定的ClassVisitor來傳遞解析後獲得的class中的信息。 parsingOptions參數表明用於解析class的選項,有幾個取值範圍:
ClassReader.SKIP_CODE:
跳過代碼屬性的標誌(我的感受就是沒有方法會被特意跳過)
ClassReader.SKIP_FRAMES:
跳過StackMap和StackMapTable屬性的標誌。跳過MethodVisitor.visitFrame方法。
ClassReader.SKIP_DEBUG:
跳過SourceFile,SourceDebugExtension,LocalVariableTable,LocalVariableTypeTable和LineNumberTable屬性的標誌。跳過ClassVisitor.visitSource, MethodVisitor.visitLocalVariable, MethodVisitor.visitLineNumber方法。
ClassReader.EXPAND_FRAMES:
用於展開堆棧映射幀的標誌。這會大大下降性能。(文檔上寫的,感受上用不到)
返回class的訪問標誌,是一個int類型的參數。
獲取類的名稱,沒什麼說的。
獲取超類的名稱,也沒啥說的。
獲取接口名稱,一樣沒啥說的。
看起來過高級了,看不懂,不知道幹啥用,不寫了。
ClassReader classReader = new ClassReader(Aoo.class.getName()); //這裏使用的匿名內部類,須要獲取class信息須要繼承重寫超類的一些方法,下面會說 classReader.accept(new ClassVisitor(Opcodes.ASM7) { { System.out.println("init ClassVisitor"); } }, ClassReader.SKIP_DEBUG); System.out.println(classReader.getClassName()); System.out.println(Arrays.toString(classReader.getInterfaces())); System.out.println(classReader.getSuperName()); System.out.println(classReader.getAccess()); //結果 init ClassVisitor com/hebaibai/example/demo/Aoo [java/io/Serializable] java/lang/Object 33
這個類是咱們獲取class信息主要用到的對象,由於是一個抽象類,咱們在使用的時候須要本身寫一個類來繼承它。須要獲得哪些信息就重寫哪些方法。
這個類方法比較多,寫幾個經常使用到的。
public ClassVisitor(int api) public ClassVisitor(int api, ClassVisitor classVisitor)
api參數指asm api版本。
classVisitor參數爲委派方法的調用對象。
訪問class的頭信息
version:class版本(編譯級別)
access: 訪問標示
name:類名稱
signature:class的簽名,多是null
superName:超類名稱
interfaces:接口的名稱
訪問class的註解信息
descriptor:描述信息
visible:是否運行時可見
訪問class中字段的信息,返回一個FieldVisitor用於獲取字段中更加詳細的信息。
name:字段個的名稱
descriptor:字段的描述
value:該字段的初始值,文檔上面說:
該參數,其能夠是零,若是字段不具備初始值,必須是一個Integer
,一Float
,一Long
,一個Double
或一個String
(對於int
,float
,long
或String
分別字段)。此參數僅用於靜態字段。對於非靜態字段,它的值被忽略,非靜態字段必須經過構造函數或方法中的字節碼指令進行初始化(可是無論我怎麼試,結果都是null)。
訪問class中方法的信息,返回一個MethodVisitor用於獲取方法中更加詳細的信息。
name:方法的名稱
descriptor:方法的描述
signature:方法的簽名
exceptions:方法的異常名稱
訪問class中內部類的信息。這個內部類不必定是被訪問類的成員(這裏的意思是多是一段方法中的匿名內部類,或者聲明在一個方法中的類等等)。
name:內部類的名稱。例子com/hebaibai/example/demo/Aoo$1XX
outerName:內部類所在類的名稱
innerName:內部類的名稱
訪問該類的封閉類。僅當類具備封閉類時,才必須調用此方法。
我本身試了一下,若是在一個方法中定義了一個class,或者定義個一個匿名內部類,這時經過visitInnerClass方法可以獲得例如com/hebaibai/example/demo/Aoo$1
或者com/hebaibai/example/demo/Aoo$1XX
的類名稱。這時經過使用
ClassReader classReader = new ClassReader("com/hebaibai/example/demo/Aoo$1"); classReader.accept(new DemoClassVisitor(Opcodes.ASM7), ClassReader.SKIP_CODE);
能夠獲得持有內部類的類信息。
owner:擁有該類的class名稱
name:包含該類的方法的名稱,若是該類未包含在其封閉類的方法中,則返回null
descriptor:描述
先寫到這裏吧~~ 有時間了補上。