- 浏览: 199906 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
budairenqin:
budairenqin 写道carlosfu 写道膜拜一下,要 ...
写个RPC娱乐一下 -
budairenqin:
carlosfu 写道膜拜一下,要是把实现过程写个博客或者文档 ...
写个RPC娱乐一下 -
dengchang1:
好文章。 详细看了《Netty源码细节1--IO线程(Even ...
Netty源码细节3--accept(Linux os层 + Netty层代码细节) -
carlosfu:
膜拜一下,要是把实现过程写个博客或者文档就更赞了
写个RPC娱乐一下 -
budairenqin:
I_am_rookie 写道你好!能把安装包发我一下吗?我找了 ...
CentOS 6.3 X86_64安装MySQL 5.5.28 64-Bit RPM以及my.cnf配置
asm是一个java的字节码框架,它能被用来动态生成类或者增强既有类的功能。
一般asm的应用场景主要在aop上,比如Spring在底层就是用了asm,但asm不仅仅是只能在aop方面发挥它强大的能力,比如你现在要写一个rpc框架,可能会在序列化对象上犯难,使用java的序列化机制?有点慢;json?(比如阿里的大神搞的fastjson性能就很好),但我要序列化的对象很简单,没有嵌套对象,不需要深拷贝,并且我并不想按照json的格式来
我的计划是将对象拼成如下格式的字符串:
用反射实现的代码大致是这样:
速度还可以,不过我觉得还可以更快,于是我想到了用asm代替反射,由于我个人对字节码的指令只是略知一二(都是从《深入java虚拟机》一书来的些理论知识),所以用那200来个字节码指令直接写一个我想要的类实在有点难度,于是我先用java写了一个我想要的类,然后用
反编译了它,然后照着反编译出来的字节码指令以及注释一步一步最后完成了一个类
下面这个类是我想要的类,我先用java完成了它
用javap反编译它,得到下面有用的信息:
上面的构造函数和write方法就是我需要的指令顺序,注意就连后面的注释信息也是很重要滴,自己意会吧,必须用得到。
接下来就模仿它来用asm直接编辑想要的类就可以了
先上类的定义以及构造函数:
仔细观察asm里的字节码指令以及参数还有Stack, Locals的值,跟javap反编译出来的一模一样哦亲
这里再说明一下,比如ALOAD指令,怎么知道它应该是MethodVisitor哪个方法的参数呢?这个很简单,参照org.objectweb.asm.Opcodes这个接口中的注释就可以了,比如:
意思是ILOAD、LLOAD、FLOAD等都是visitVarInsn方法的参数
然后上最关键的,我需要的write方法的代码
相关的类装载器就不贴出来了,主要是将defineClass公开出来就可以了
FieldInfo用来存放要序列化的类的对应信息,数据结构如下:
打开代码中被注释掉的代码,将生成的class文件写入到磁盘中,然后用JD等反编译工具看看成果吧
生成的这个类需要缓存下来,这样相同类型的对象序列化是就不需要重新生成新类了,具体跟这次的内容无关,就不细说了
接下来要按照同样的思路来生成反序列化的类就可以了,相同的道理,不重复了
最后,经过测试,比反射的效率要高,由于我生成的序列化以及反序列化类非常的适合我目前的需求,没有一点多余的代码,对于我的应用场景来说,也比使用fastjson将对象序列化成json要快一些,总之适合自己的就是最好的
后续:我山炮了,asm官网有个插件http://asm.ow2.org/eclipse/index.html
安装以后基本能直接生成代码,左边代码直接拷贝过来即可,如下图:
一般asm的应用场景主要在aop上,比如Spring在底层就是用了asm,但asm不仅仅是只能在aop方面发挥它强大的能力,比如你现在要写一个rpc框架,可能会在序列化对象上犯难,使用java的序列化机制?有点慢;json?(比如阿里的大神搞的fastjson性能就很好),但我要序列化的对象很简单,没有嵌套对象,不需要深拷贝,并且我并不想按照json的格式来
我的计划是将对象拼成如下格式的字符串:
fieldName1,Type1,value1;fieldName2,Type2,value2;fieldName3,Type3,value3;fieldName4,Type4,value4;...;
用反射实现的代码大致是这样:
for (Map.Entry<String, Pair<String, Field>> iter : argFields.entrySet()) { fieldName = iter.getKey(); // 名 pair = iter.getValue(); fieldType = pair.getKey(); // 类型 fieldValue = pair.getValue().get(event); // 值(反射) if (fieldValue instanceof Date) { sBuilder.append(fieldName).append(",").append(fieldType).append(",").append(((Date) fieldValue).getTime()).append(";"); } else { sBuilder.append(fieldName).append(",").append(fieldType).append(",").append(fieldValue).append(";"); } }
速度还可以,不过我觉得还可以更快,于是我想到了用asm代替反射,由于我个人对字节码的指令只是略知一二(都是从《深入java虚拟机》一书来的些理论知识),所以用那200来个字节码指令直接写一个我想要的类实在有点难度,于是我先用java写了一个我想要的类,然后用
javap -verbose XXX
反编译了它,然后照着反编译出来的字节码指令以及注释一步一步最后完成了一个类
下面这个类是我想要的类,我先用java完成了它
public StringBuilder write(Event event) throws IOException { GNSEvent obj = (GNSEvent) event; StringBuilder sBuilder = new StringBuilder(256); sBuilder.append("cityCode").append(",").append("java.lang.String").append(obj.getCityCode()); // obj循环序列化obj的所有属性 return sBuilder; }
用javap反编译它,得到下面有用的信息:
public com.futurefleet.framework.serialization.Serializer_1GNSEvent(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #10; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 16: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/futurefleet/framework/serialization/Serializer_1GNSEvent; public java.lang.StringBuilder write(com.futurefleet.framework.base.event.Event) throws java.io.IOException; Exceptions: throws java.io.IOException Code: Stack=3, Locals=4, Args_size=2 0: aload_1 1: checkcast #21; //class com/futurefleet/gateway/event/GNSEvent 4: astore_2 5: new #23; //class java/lang/StringBuilder 8: dup 9: sipush 256 12: invokespecial #25; //Method java/lang/StringBuilder."<init>":(I)V 15: astore_3 16: aload_3 17: ldc #28; //String cityCode 19: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: ldc #34; //String , 24: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: ldc #36; //String java.lang.String 29: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: aload_2 33: invokevirtual #38; //Method com/futurefleet/gateway/event/GNSEvent.getCityCode:()Ljava/lang/String; 36: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: pop 40: aload_3 41: areturn
上面的构造函数和write方法就是我需要的指令顺序,注意就连后面的注释信息也是很重要滴,自己意会吧,必须用得到。
接下来就模仿它来用asm直接编辑想要的类就可以了
先上类的定义以及构造函数:
ClassWriter classWriter = new ClassWriter(0); classWriter.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", new String[] { "com/futurefleet/framework/serialization/EventSerializer" }); // 构造函数 MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(1, 1); methodVisitor.visitEnd();
仔细观察asm里的字节码指令以及参数还有Stack, Locals的值,跟javap反编译出来的一模一样哦亲
这里再说明一下,比如ALOAD指令,怎么知道它应该是MethodVisitor哪个方法的参数呢?这个很简单,参照org.objectweb.asm.Opcodes这个接口中的注释就可以了,比如:
int ILOAD = 21; // visitVarInsn int LLOAD = 22; // - int FLOAD = 23; // - int DLOAD = 24; // - int ALOAD = 25; // -
意思是ILOAD、LLOAD、FLOAD等都是visitVarInsn方法的参数
然后上最关键的,我需要的write方法的代码
// StringBuilder write(Event event) throws IOException; { methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "write", "(Lcom/futurefleet/framework/base/event/Event;)Ljava/lang/StringBuilder;", null, new String[] { "java/io/IOException" }); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitTypeInsn(CHECKCAST, eventTypeString); methodVisitor.visitVarInsn(ASTORE, 2); methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder"); methodVisitor.visitInsn(DUP); methodVisitor.visitIntInsn(SIPUSH, 256); methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(I)V"); methodVisitor.visitVarInsn(ASTORE, 3); methodVisitor.visitVarInsn(ALOAD, 3); FieldInfo fieldInfo = null; Class<?> fieldClass = null; for (Map.Entry<String, FieldInfo> iter : getters.entrySet()) { fieldInfo = iter.getValue(); fieldClass = TypeUtils.getClass(fieldInfo.getFieldType()); methodVisitor.visitLdcInsn(fieldInfo.getName()); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); methodVisitor.visitLdcInsn(","); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); methodVisitor.visitLdcInsn(TypeUtils.getClass(fieldInfo.getFieldType()).getName()); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); methodVisitor.visitLdcInsn(","); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); methodVisitor.visitVarInsn(ALOAD, 2); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, eventTypeString, fieldInfo.getMethod().getName(), TypeUtils.getDesc(fieldInfo.getMethod())); if (fieldClass == Date.class || fieldClass == java.sql.Date.class || fieldClass == java.sql.Time.class || fieldClass == java.sql.Timestamp.class) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, TypeUtils.getType(fieldClass), "getTime", "()J"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;"); } else { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + TypeUtils.getDesc(TypeUtils.getClass(fieldInfo.getFieldType())) + ")Ljava/lang/StringBuilder;"); } methodVisitor.visitLdcInsn(";"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); } methodVisitor.visitInsn(POP); methodVisitor.visitVarInsn(ALOAD, 3); methodVisitor.visitInsn(ARETURN); methodVisitor.visitMaxs(3, 4); methodVisitor.visitEnd(); } byte[] classCode = classWriter.toByteArray(); // { // FileOutputStream fos = new FileOutputStream(new File("/Users/fengjiachun/Documents/" + className + ".class")); // fos.write(classCode, 0, classCode.length); // } Class<?> asmClass = this.classLoader.defineClassPublic(className, classCode, 0, classCode.length);
相关的类装载器就不贴出来了,主要是将defineClass公开出来就可以了
FieldInfo用来存放要序列化的类的对应信息,数据结构如下:
private final String name; // 字段名称 private final Method method; // 对应方法(序列化是为getter,反序列化时为setter) private final Field field; // 字段
打开代码中被注释掉的代码,将生成的class文件写入到磁盘中,然后用JD等反编译工具看看成果吧
public class Serializer_1GNSEvent implements EventSerializer { public StringBuilder write(Event paramEvent) throws IOException { GNSEvent localGNSEvent = (GNSEvent)paramEvent; StringBuilder localStringBuilder = new StringBuilder(256); localStringBuilder.append("serverTimeStamp").append(",").append("long").append(",").append(localGNSEvent.getServerTimeStamp()).append(";").append("hasOffset").append(",").append("java.lang.String").append(",").append(localGNSEvent.getHasOffset()).append(";").append("clientTimeStamp").append(",").append("long").append(",").append(localGNSEvent.getClientTimeStamp()).append(";").append("liveCity").append(",").append("java.lang.String").append(",").append(localGNSEvent.getLiveCity()).append(";").append("cityCode").append(",").append("java.lang.String").append(",").append(localGNSEvent.getCityCode()).append(";").append("lng").append(",").append("double").append(",").append(localGNSEvent.getLng()).append(";").append("type").append(",").append("int").append(",").append(localGNSEvent.getType()).append(";").append("lat").append(",").append("double").append(",").append(localGNSEvent.getLat()).append(";").append("version").append(",").append("java.lang.String").append(",").append(localGNSEvent.getVersion()).append(";").append("parseSucceed").append(",").append("boolean").append(",").append(localGNSEvent.isParseSucceed()).append(";"); return localStringBuilder; } }
生成的这个类需要缓存下来,这样相同类型的对象序列化是就不需要重新生成新类了,具体跟这次的内容无关,就不细说了
接下来要按照同样的思路来生成反序列化的类就可以了,相同的道理,不重复了
最后,经过测试,比反射的效率要高,由于我生成的序列化以及反序列化类非常的适合我目前的需求,没有一点多余的代码,对于我的应用场景来说,也比使用fastjson将对象序列化成json要快一些,总之适合自己的就是最好的
后续:我山炮了,asm官网有个插件http://asm.ow2.org/eclipse/index.html
安装以后基本能直接生成代码,左边代码直接拷贝过来即可,如下图:
发表评论
-
JUC中Atomic class之lazySet的一点疑惑
2015-11-15 20:45 930发在并发编程网了 http://ifeve.com/juc-a ... -
JUC中Atomic class之lazySet的一点疑惑
2015-06-19 01:27 0最近再次翻netty和disrupt的源码, 发现一些地方使用 ... -
Java8中用sun.misc.Contended避免伪共享(false sharing)
2014-04-18 13:58 4146关于伪共享这个概念,请先参照http://ifeve.co ... -
Java 绕过编译器检查抛出“受检查的”异常
2014-01-23 16:45 2249个别特殊情况下,我们 ... -
远程执行小工具
2013-02-04 23:49 2518今天想给项目写个远程执行的小工具 1.客户端动态编译要远 ... -
Web应用下JVM从哪个类包加载指定类
2012-08-29 22:50 0以下内容来自《Spring3.x企业应用开发实战》一书,收藏下 ... -
Java虚拟机指令操作码助记符
2012-06-21 17:36 2572以下内容均来自IcyFenix等大牛翻译的Java虚拟机规范 ... -
自定义的类装载器-从DB装载class(附上对类装载器的分析)
2011-11-25 14:31 412代码才是最实在的,先从代码开始,然后再一步一步分析: 第一步: ... -
java安全管理器-SecurityManager
2011-11-23 15:16 3403当java应用程序启动时,它还没有安全管理器,应用程序可以通过 ... -
jvm学习笔记-class文件检验器
2011-11-23 03:33 410唉,记忆力衰退了吗? ...
相关推荐
ASM Java字节码操作框架PPT,结合已有AOP实现方法,对比所有对Java字节码操作方法做比较
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class ...
NULL 博文链接:https://xpenxpen.iteye.com/blog/2194267
主要给大家介绍了关于java字节码框架ASM如何操作字节码的相关资料,文中通过示例代码介绍的很详细,有需要的朋友可以参考借鉴,下面来一起看看吧。
主要给大家介绍了java中字节码框架ASM的相关资料,文中介绍的非常详细,相信对大家的理解和学习具有一定的参考借鉴价值,有需要的朋友们下面来一起学习学习吧。
字节码生成库是生成和转换java字节码的高级api。
ASM操作字节码,动态生成Java类class文件,模拟Spring的AOP实现原理。
ASM 帮助文档(java字节码操作) 对字节码进行操作的jar包。
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class...
一个简单的通过ASM修改字节码实现AOP功能的实例,简单易懂,可运行...
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class ...
org.openjdk.asmtools.jdis.Main Java字节码的反汇编器工具 Java字节码的反汇编器工具
ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和代码分析工具。ASM提供与其他Java字节码...
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class ...
埋点计时Gradle插件,利用ASM插入字节码,对指定包名内的类或指定注解的方法,打印其方法的耗时时间。
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件...
ASM是一个通用的Java字节码操作和分析框架。 它可以用于修改现有类或直接以二进制形式动态生成类。 ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和代码分析工具。 ASM提供与其他Java字节码...
asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。 而这里说到的asm是指objectweb asm,一种.class的代码生成器的开源项目. ASM是一套java字节码生成架构,它可以动态生成二进制格式的...
内容包含ASM4.0中文手册,以及四种ASM常见的字节码操作应用范例,包含最新版本的ASM9.2的jar包,包含asm-9.2.jar,asm-commons-9.2.jar,asm-util-9.2.jar。 学习文章地址 ...
ASM 4 的中文文档 网上的博文都是从这上边摘得,大家下载下来学学就会了,这个是最全的了。我就是通过这个学会的。