比反射更快!使用ASM获取class信息(ClassReader)

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

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中的信息。

从官网抄的介绍:

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

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

嗯~

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

添加依赖

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>7.1</version>
</dependency>

读取class需要的对象

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

因为我们要做的是获取class中的各种信息,所以我们需要用到下面一些对象:

  1. ClassReader :按照Java虚拟机规范中定义的方式来解析class文件中的内容,在遇到合适的字段时调用ClassVisitor中相对应的方法。
  2. ClassVisitor:java中的访问者,提供一系列方法由ClassReader调用。是一个抽象类,我们在使用的时候需要继承此类。使用此对象的时候需要指定asm api的版本。
  3. ModuleVisitor:Java中模块的访问者,作为ClassVisitor.visitModule方法的返回值,要是不关心模块的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
  4. AnnotationVisitor:Java中注解的访问者,作为ClassVisitovisitTypeAnnotationvisitTypeAnnotation的返回值,要是不关心注解的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
  5. FieldVisitor:Java中字段的访问者,作为ClassVisito.visitField的返回值,要是不关心字段的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
  6. 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."<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修饰的类。

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(对于intfloatlong 或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:描述

3:其他的对象

先写到这里吧~~ 有时间了补上。

没了~

原文地址: https://www.cnblogs.com/hebaibai/p/11011004.html

比反射更快!使用ASM获取class信息(ClassReader)的更多相关文章

  1. 利用Java反射根据类的名称获取属性信息和父类的属性信息

    代码: import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java ...

  2. JAVA获取时间戳,哪个更快

    目前获取毫秒值大概有下面三种方法 //方法 一 System.currentTimeMillis(); //方法 二 Calendar.getInstance().getTimeInMillis(); ...

  3. 使用QFileInfo类获取文件信息(在NTFS文件系统上,出于性能考虑,文件的所有权和权限检查在默认情况下是被禁用的,通过qt_ntfs_permission_lookup开启和操作。absolutePath()必须查询文件系统。而path()函数,可以直接作用于文件名本身,所以,path() 函数的运行会更快)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/65444966QFileInfo类为我们提供了系统无 ...

  4. Quick UDP Internet Connections 让互联网更快的协议,QUIC在腾讯的实践及性能优化

    https://mp.weixin.qq.com/s/44ysXnVBUq_nJByMyX9n5A 让互联网更快:通往QUIC之路 原创: 史天 翻译 云技术实践 8月15日 QUIC(Quick U ...

  5. 精通Web Analytics 2.0 (9) 第七章:失败更快:爆发测试与实验的能量

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第七章:失败更快:爆发测试与实验的能量 欢迎来到实验和测试这个棒极了的世界! 如果Web拥有一个超越所有其他渠道的巨大优势,它就 ...

  6. php提供更快的文件下载

    在微博上偶然看到一篇介绍php更快下载文件的方法,其实就是利用web服务器的xsendfile特性,鸟哥的博客中只说了apache的实现方式,我找到了介绍nginx实现方式的文章,整理一下! let' ...

  7. 为什么get比post更快

    引言 get和post在面试过程中一般都会问到,一般的区别: 1.post更安全(不会作为url的一部分,不会被缓存.保存在服务器日志.以及浏览器浏览记录中) 2.post发送的数据量更大(get有u ...

  8. CSS VS JS动画,哪个更快[译]

    英文原文:https://davidwalsh.name/css-js-animation 原作者Julian Shapiro是Velocity.js的作者,Velocity.js是一个高效易用的js ...

  9. 更快、更强——解析Hadoop新一代MapReduce框架Yarn(CSDN)

    摘要:本文介绍了Hadoop 自0.23.0版本后新的MapReduce框架(Yarn)原理.优势.运作机制和配置方法等:着重介绍新的Yarn框架相对于原框架的差异及改进. 编者按:对于业界的大数据存 ...

随机推荐

  1. codevs 3022 西天收费站 x

                         题目描述 Description 唐僧师徒四人终于发现西天就在眼前,但猴子突然发现前面有n个收费站(如来佛太可恶),在每个收费站用不同的方式要交的钱不同,输入 ...

  2. 本地Git连接远程Gitlab

    本地端安装https://www.cnblogs.com/wei9593/p/11698204.html 1.打开本地git bash,使用如下命令生成ssh公钥和私钥 ssh-keygen -t r ...

  3. Android学习_7/27

    一.           自定义控件 1.         引入布局 多个活动需要相同的布局时,使用引入布局的方式来实现代码复用. activity_main.xlm中加入<include la ...

  4. LightGBM GPU python版本安装

    失败的安装尝试 1.官方Guide https://lightgbm.readthedocs.io/en/latest/GPU-Windows.html 生成在windows下可执行的exe程序,但是 ...

  5. Bzoj3073Journeys

    这不裸的dij吗?来,弄他. 打完以后发现不妙,这数据范围略神奇……算一算,考一场都可能跑不出来.map去重边(成功额外引入log)不怕,交.TLE,54. 这不玩呢吗,把map去了,交.MLE,71 ...

  6. C++中void和void*指针的含义 (指针类型的含义)

    转载自:http://blog.csdn.net/lee_shuai 指针有两个属性:指向变量/对象的地址和长度,但是指针只存储地址,长度则取决于指针的类型:编译器根据指针的类型从指针指向的地址向后寻 ...

  7. DMA数据传输

    从字面意思上看,DMA即为“直接内存读取”的意思,换句话说DMA就是用来传输数据的,它也属于一个外设.只是在传输数据时,无需占用CPU. 高速IO设备可以在处理器安排下直接与主存储器成批交换数据,称为 ...

  8. 20165207 Exp9 Web安全基础

    目录 20165207 Exp9 Web安全基础 一.实验过程 1.环境配置 2.代理工具burpsuite 2.1 Http proxies -> Use the intercept 3.sq ...

  9. CSS、Bulma介绍

    文章目录 一.序章 二.CSS 基础 1. CSS 介绍 2. CSS 语法 3. CSS常用元素 1.颜色 2.字体大小 3.宽高 4.盒模型(单独拿出来讲) 5.背景 4.1样式和内容分离 4.2 ...

  10. mysql 创建相同的表结构

    前言: 项目中用到分表存储,需要创建100张表,每个表的结构相同,原始操作,一个个复制粘贴,修改名字.今天DBA给了意见 create table a like b 将b的表结构和索引都复制  cre ...