一般咱们想要在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:描述
先写到这里吧~~ 有时间了补上。