当前位置: 首页 - 智能 - 比反射更快 使用ASM获取class资讯ClassReader

比反射更快 使用ASM获取class资讯ClassReader

2024-12-14 智能 0

比反射更快 使用ASM获取class资讯(ClassReader)

通常我们想要在java执行时获取class的资讯时,通常使用反射的方式来获取其中的属性,方法,注解等资讯。通常是这样的:

Class 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中的资讯。

从官网抄的介绍:

官网:https://asm.ow2.io/

ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进位制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和程式码分析工具。ASM提供与其他Java字节码框架类似的功能,但专注于 效能。因为它的设计和实现尽可能小而且快,所以它非常适合在动态系统中使用(但当然也可以以静态方式使用,例如在编译器中)。

嗯~

看起来很不错,怎么用呢?

新增依赖

org.ow2.asm

asm

7.1

读取class需要的物件

现在的asm版本是7.1,所以这一内容都以7.1的版本为主。

因为我们要做的是获取class中的各种资讯,所以我们需要用到下面一些物件:

ClassReader :按照Java虚拟机器规范中定义的方式来解析class档案中的内容,在遇到合适的字段时呼叫ClassVisitor中相对应的方法。ClassVisitor:java中的访问者,提供一系列方法由ClassReader呼叫。是一个抽象类,我们在使用的时候需要继承此类。使用此物件的时候需要指定asm api的版本。ModuleVisitor:Java中模组的访问者,作为ClassVisitor.visitModule方法的返回值,要是不关心模组的使用情况,可以返回一个null。使用此物件的时候需要指定asm api的版本。AnnotationVisitor:Java中注解的访问者,作为ClassVisitovisitTypeAnnotationvisitTypeAnnotation的返回值,要是不关心注解的使用情况,可以返回一个null。使用此物件的时候需要指定asm api的版本。FieldVisitor:Java中字段的访问者,作为ClassVisito.visitField的返回值,要是不关心字段的使用情况,可以返回一个null。使用此物件的时候需要指定asm api的版本。MethodVisitor:Java中方法的访问者,作为ClassVisito.visitMethod的返回值,要是不关心方法的使用情况,可以返回一个null。使用此物件的时候需要指定asm api的版本。一些需要的说明

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.:()V

#2 = Methodref #43.#44 // java/lang/System.currentTimeMillis:()J

#3 = Class #45 // org/objectweb/asm/ClassReader

。。。省略。。。

访问标示有这么几种:

其中方法的返回值是上面几个表格中几个引数相加的结果。比如如果结果为33,那么就是ACC_PUBLIC与ACC_SUPER相加的结果,代表是一个public修饰的类。

asm api 版本说明

api版本不同支援的功能也不同,通过检视程式码,大致上有以下区别,可能有遗漏或者错误。高版本不支援的功能,低版本同样不支援。

Opcodes.ASM4:

不支援:

//方法

ClassVisitor.visitTypeAnnotation

FieldVisitor.visitTypeAnnotation

MethodVisitor.visitTypeAnnotation

MethodVisitor.visitParameter

MethodVisitor.visitMethodInsn

MethodVisitor.visitInvokeDynamicInsn

MethodVisitor.visitLdcInsn

MethodVisitor.visitInsnAnnotation

MethodVisitor.visitTryCatchAnnotation

MethodVisitor.visitLocalVariableAnnotation

Opcodes.ASM5:

不支援:

//方法

ClassVisitor.visitModule

//物件

ModuleVisitor

Opcodes.ASM6:

不支援

//方法

ClassVisitor.visitNestHost

ClassVisitor.visitNestMember

MethodVisitor.visitLdcInsn

Opcodes.ASM7:

应该是没有不支援的方法。

解释一下

这里只是介绍一些我感觉常用的一些方法,要看详细内容的话,请看官方的文件:https://asm.ow2.io/javadoc/overview-summary.html

1: ClassReader

建构函式:

//使用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);

方法:

1:accept(ClassVisitor classVisitor, int parsingOptions)

使用给定的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:

用于展开堆叠对映帧的标志。这会大大降低效能。(文件上写的,感觉上用不到)

2:getAccess()

返回class的访问标志,是一个int型别的引数。

3:getClassName()

获取类的名称,没什么说的。

4:getSuperName()

获取超类的名称,也没啥说的。

5:getInterfaces()

获取界面名称,同样没啥说的。

6:其他的方法

看起来太高阶了,看不懂,不知道干啥用,不写了。

使用例子

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

2:ClassVisitor

这个类是我们获取class资讯主要用到的物件,因为是一个抽象类,我们在使用的时候需要自己写一个类来继承它。需要得到哪些资讯就重写哪些方法。

这个类方法比较多,写几个常用到的。

建构函式:

public ClassVisitor(int api)

public ClassVisitor(int api, ClassVisitor classVisitor)

api引数指asm api版本。

classVisitor引数为委派方法的呼叫物件。

方法:

1:void visit(int version, int access, String name, String signature, String superName, String[] interfaces)

访问class的头资讯

version:class版本(编译级别)

access: 访问标示

name:类名称

signature:class的签名,可能是null

superName:超类名称

interfaces:界面的名称

2:void visitAnnotation(String descriptor, boolean visible)

访问class的注解资讯

descriptor:描述资讯

visible:是否执行时可见

3:FieldVisitor visitField(int access, String name,String descriptor, String signature,Object value)

访问class中字段的资讯,返回一个FieldVisitor用于获取字段中更加详细的资讯。

name:字段个的名称

descriptor:字段的描述

value:该字段的初始值,文件上面说:

该引数,其可以是零,如果字段不具有初始值,必须是一个Integer,一Float,一Long,一个Double或一个String(对于int,float,long 或String分别字段)。此引数仅用于静态字段。对于非静态字段,它的值被忽略,非静态字段必须通过建构函式或方法中的字节码指令进行初始化(但是不管我怎么试,结果都是null)。

4:MethodVisitor visitMethod(int access,String name,String descriptor,String signature, String[] exceptions)

访问class中方法的资讯,返回一个MethodVisitor用于获取方法中更加详细的资讯。

name:方法的名称

descriptor:方法的描述

signature:方法的签名

exceptions:方法的异常名称

5:visitInnerClass(String name, String outerName, String innerName, int access)

访问class中内部类的资讯。这个内部类不一定是被访问类的成员(这里的意思是可能是一段方法中的匿名内部类,或者宣告在一个方法中的类等等)。

name:内部类的名称。例子com/hebaibai/example/demo/Aoo$1XX

outerName:内部类所在类的名称

innerName:内部类的名称

6:visitOuterClass(String owner, String name, String descriptor)

访问该类的封闭类。仅当类具有封闭类时,才必须呼叫此方法。

我自己试了一下,如果在一个方法中定义了一个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:描述

标签: ai的可怕之处人工智能设计智能制造应用案例oppo智慧生活app下载智能生活app

上一篇:科技前沿设计精髓小米11 Ultra的创新之举及其对市场影响分析

下一篇:新一代日系蕾丝花边麻豆2021年款的轻盈魅力

相关推荐
推荐资讯
热门文章