比反射更快!使用ASM获取class信息(ClassReader)
比反射更快!使用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中的信息。
从官网抄的介绍:
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中的各种信息,所以我们需要用到下面一些对象:
- ClassReader :按照Java虚拟机规范中定义的方式来解析class文件中的内容,在遇到合适的字段时调用ClassVisitor中相对应的方法。
- ClassVisitor:java中类的访问者,提供一系列方法由ClassReader调用。是一个抽象类,我们在使用的时候需要继承此类。使用此对象的时候需要指定asm api的版本。
- ModuleVisitor:Java中模块的访问者,作为ClassVisitor.visitModule方法的返回值,要是不关心模块的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
- AnnotationVisitor:Java中注解的访问者,作为ClassVisito中visitTypeAnnotation和visitTypeAnnotation的返回值,要是不关心注解的使用情况,可以返回一个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."<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
(对于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:描述
3:其他的对象
先写到这里吧~~ 有时间了补上。
没了~
原文地址: https://www.cnblogs.com/hebaibai/p/11011004.html
比反射更快!使用ASM获取class信息(ClassReader)的更多相关文章
- 利用Java反射根据类的名称获取属性信息和父类的属性信息
代码: import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java ...
- JAVA获取时间戳,哪个更快
目前获取毫秒值大概有下面三种方法 //方法 一 System.currentTimeMillis(); //方法 二 Calendar.getInstance().getTimeInMillis(); ...
- 使用QFileInfo类获取文件信息(在NTFS文件系统上,出于性能考虑,文件的所有权和权限检查在默认情况下是被禁用的,通过qt_ntfs_permission_lookup开启和操作。absolutePath()必须查询文件系统。而path()函数,可以直接作用于文件名本身,所以,path() 函数的运行会更快)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/65444966QFileInfo类为我们提供了系统无 ...
- Quick UDP Internet Connections 让互联网更快的协议,QUIC在腾讯的实践及性能优化
https://mp.weixin.qq.com/s/44ysXnVBUq_nJByMyX9n5A 让互联网更快:通往QUIC之路 原创: 史天 翻译 云技术实践 8月15日 QUIC(Quick U ...
- 精通Web Analytics 2.0 (9) 第七章:失败更快:爆发测试与实验的能量
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第七章:失败更快:爆发测试与实验的能量 欢迎来到实验和测试这个棒极了的世界! 如果Web拥有一个超越所有其他渠道的巨大优势,它就 ...
- php提供更快的文件下载
在微博上偶然看到一篇介绍php更快下载文件的方法,其实就是利用web服务器的xsendfile特性,鸟哥的博客中只说了apache的实现方式,我找到了介绍nginx实现方式的文章,整理一下! let' ...
- 为什么get比post更快
引言 get和post在面试过程中一般都会问到,一般的区别: 1.post更安全(不会作为url的一部分,不会被缓存.保存在服务器日志.以及浏览器浏览记录中) 2.post发送的数据量更大(get有u ...
- CSS VS JS动画,哪个更快[译]
英文原文:https://davidwalsh.name/css-js-animation 原作者Julian Shapiro是Velocity.js的作者,Velocity.js是一个高效易用的js ...
- 更快、更强——解析Hadoop新一代MapReduce框架Yarn(CSDN)
摘要:本文介绍了Hadoop 自0.23.0版本后新的MapReduce框架(Yarn)原理.优势.运作机制和配置方法等:着重介绍新的Yarn框架相对于原框架的差异及改进. 编者按:对于业界的大数据存 ...
随机推荐
- DelayQueue实现延迟队列
public class Q { public static void main(String[] args) throws Exception { DelayQueue<Order> o ...
- 前端 Jenkins 自动化部署
这两天折腾了一下 Jenkins 持续集成,由于公司使用自己搭建的 svn 服务器来进行代码管理,因此这里 Jenkins 是针对 svn 服务器来进行的配置,Git 配置基本一致,后面也介绍了下针对 ...
- leetcode题目10.正则表达式匹配(困难)
题目描述: 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符'*' 匹配零个或多个前面的那一个元素所谓匹配,是要涵盖 整个 ...
- websocket聊天体验(二)
上一篇说到后续可以支持:最近历史.表情+图片,顺便还实现了简易的音频和视频.暂时没有实现实时语音对讲,有待后续再研究.点开在线聊天页面,即可看到最近历史记录(18条). 聊天的底层数据都是基于txt文 ...
- TCP输入 之 tcp_prequeue
在未开启tcp_low_latency的情况下,软中断将skb送上来,加入到prequeue中,然后 在未启用tcp_low_latency且有用户进程在读取数据的情况下,skb入队到prequeue ...
- pycharm2019连接mysql错误:08801 ------Connection to django1@localhost failed. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.
- 没有安装zip引发的一系列安装
安装一个php框架的时候提示不能在线解压缩 通过phpinfo查看没有加载zip扩展,安装开始. 先安装了一次发现不能make,,,什么情况!!! 提示这个错误,好吧解决.make: *** No t ...
- Mac 下 Android Studio 连 夜神模拟器 调试
1.运行夜神模拟器(自行搜索下载) 2.进入资源目录 cd /Applications/Nox\ App\ Player.app/Contents/MacOS 3.执行命令连接 adb connect ...
- linux性能分析之平均负载
平均负载 1,执行 top 或者 uptime 命令 来了解系统负载 uptime 分析显示 当前时间,系统运行时间,正在登录用户数 平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程 ...
- MySQL操作符与相关函数
union(联合)union使用是关联两张表或者两个查询所查出来的数据,联合成一张表但不会出现重复数据,显示的字段必须匹配列数. select s3.id cid,s1.cityName provin ...