- 浏览: 201483 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
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 948发在并发编程网了 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 4239关于伪共享这个概念,请先参照http://ifeve.co ... -
Java 绕过编译器检查抛出“受检查的”异常
2014-01-23 16:45 2277个别特殊情况下,我们 ... -
远程执行小工具
2013-02-04 23:49 2572今天想给项目写个远程执行的小工具 1.客户端动态编译要远 ... -
Web应用下JVM从哪个类包加载指定类
2012-08-29 22:50 0以下内容来自《Spring3.x企业应用开发实战》一书,收藏下 ... -
Java虚拟机指令操作码助记符
2012-06-21 17:36 2600以下内容均来自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字节码操控和分析框架,它可以直接用来生成和修改Java类文件,是Java动态代理和字节码增强技术的重要工具。在深入学习Java字节码和ASM之前,我们需要先理解Java编译和运行的基本过程。 1. **...
首先,Javassist是一个开源的Java字节码操作框架,它使得开发者可以在运行时或编译时方便地修改Java类。通过Javassist,我们能够动态创建新的类、接口,或者修改已存在的类的结构,包括添加、修改或删除方法和字段。...
ASM是一个强大的Java字节码操控和分析框架,它允许开发者动态生成或修改Java类和运行时的类。这个“ASM使用指南-中文版”提供了全面的教程和参考信息,帮助开发者深入理解并有效地利用ASM库。 ASM的核心功能在于...
ASM是Java字节码操作和分析框架,它允许程序员在运行时动态生成和修改类和方法。ASM的主要用途包括创建动态代理、代码分析、代码优化以及AOP(面向切面编程)实现。以下是对ASM这一高级Java技术的详细解读: 1. **...
ByteBuddy是一个强大的字节码库,它允许开发者在不使用Java代理(Java Proxy)或者ASM等底层字节码库的情况下,便捷地创建和修改Java类与接口。本资源主要关注如何使用ByteBuddy来创建和处理注解,这对于理解和实现...
它底层基于ASM库,但为开发者提供了更友好的API,简化了对Java字节码的操作。 CGLIB的核心功能包括以下几点: 1. **Proxy实现**:CGLIB提供了动态代理的功能,可以创建目标类的子类,从而实现代理。这种代理方式比...
9. **asm-3.3.1.jar**:ASM是一个Java字节码操控和分析框架,MyBatis可能在某些情况下使用ASM来直接操作和分析字节码,如动态代理的生成。 这些jar包构成了MyBatis基本运行环境,开发者可以直接将它们导入到项目中...
- `cglib-nodep.jar`和`asm.jar`:这两个库通常用于动态代理和字节码操作,Hibernate在处理实体类的动态代理时可能会用到。 - `dom4j.jar`或`jdom.jar`:XML处理库,Hibernate的HQL查询结果集转换为XML时会用到。 ...
卢阿 LuaJ,但速度更快这是LuaJ的Lua到Java字节码编译器的分支。 它已转换为使用ASM框架,并且已修复了许多错误。与原来的不同核心调试支持( debug.traceback , debug.getinfo和所有调试钩子-尽管您无法获取或更改...
- **asm-3.3.jar**:操作 Java 字节码的类库。 - **commons-fileupload-1.3.2.jar**:文件上传支持。 - **freemarker-2.3.22.jar**:模板引擎,用于生成文本输出。 - **javassist-3.11.0.GA.jar**:用于分析、...
ASM 是一个 Java 字节码操控和分析框架,它可以直接生成和修改 Java 类的字节码。这个库被广泛用于动态代理、代码混淆、性能监控以及字节码级别的元编程。ASM 提供了底层 API,允许开发者对类的结构进行精细控制,...
2. **基于字节码增强的AOP**:通过ASM库,Spring可以在运行时修改字节码,将切面代码织入目标类,实现更高效和灵活的AOP操作。 ### 四、Spring AOP的配置和使用 在Spring中,我们可以通过XML配置或注解的方式来...
- **javassist**: 字节码操作库。 #### 1.4 Struts2入门理论 **JavaEE设计模式** 对于理解Struts2的工作原理非常重要。其中,**前端控制器模式**是Struts2中最为关键的设计模式之一。这种模式的主要作用是集中处理...
基于代理的织入通常用于Spring的IoC容器中的bean,而基于字节码的织入则是在运行时通过ASM库动态修改类的字节码来实现,这正是@AspectJ所采用的方式。 @AspectJ是Spring AOP的一个扩展,它提供了一种更接近传统编程...
很多人觉得它应该像C或者C++,但事实上它更像是java的一个clone,所以作为入门,读一下清华大学出版社出版的《Java 语言与面向对象程序设计》可能会对你有所帮助。本文假定你具备一切学习此语言所需的知识,没有也不...