ASM字节码增强技术
ASM是什么?
asm是字节码增强技术,通过asm可以生成新的class文件,也可以动态的修改即将要装载入jvm的类信息。
一、什么是ASM
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。asm字节码增强技术主要是用来反射的时候提升性能的,如果单纯用jdk的反射调用,性能是非常低下的,而使用字节码增强技术后反射调用的时间已经基本可以与直接调用相当了
使用ASM框架需要导入asm的jar包,下载链接:asm-3.2.jar。
二、如何使用ASM
ASM框架中的核心类有以下几个:
① ClassReader:该类用来解析编译过的class字节码文件。
② ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
③ ClassAdapter:该类也实现了ClassVisitor接口,它将对它的方法调用委托给另一个ClassVisitor对象。
三、 ASM字节码处理框架是用Java开发的而且使用基于访问者模式生成字节码及驱动类到字节码的转换,通俗的讲,它就是对class文件的CRUD,经过CRUD后的字节码可以转换为类。ASM的解析方式类似于SAX解析XML文件,它综合运用了访问者模式、职责链模式、桥接模式等多种设计模式,相对于其他类似工具如BCEL、SERP、Javassist、CGLIB,它的最大的优势就在于其性能更高,其jar包仅30K。Hibernate和Spring都使用了cglib代理,而cglib本身就是使用的ASM,可见ASM在各种开源框架都有广泛的应用。
ASM是一个强大的框架,利用它我们可以做到:
1、获得class文件的详细信息,包括类名、父类名、接口、成员名、方法名、方法参数名、局部变量名、元数据等
2、对class文件进行动态修改,如增加、删除、修改类方法、在某个方法中添加指令等
CGLIB(动态代理)是对ASM的封装,简化了ASM的操作,降低了ASM的使用门槛,
其中,hibernate的懒加载使用到了asm,spring的AOP也使用到了。你建立一个hibernate映射对象并使用懒加载配置的时候,在内存中生成的对象使用的不再是你实现的那个类了,而是hibernate根据字节码技术已你的类为模板构造的一个新类,证明就是当你获得那个对象输出类名是,不是你自己生成的类名了。spring可能是proxy$xxx,hibernate可能是<你的类名>$xxx$xxx之类的名字。
AOP 的利器:ASM 3.0 介绍
随着 AOP(Aspect Oriented Programming)的发展,代码动态生成已然成为 Java 世界中不可或缺的一环。本文将介绍一种小巧轻便的 Java 字节码操控框架 ASM,它能方便地生成和改造 Java 代码。著名的框架,如 Hibernate 和 Spring 在底层都用到了 ASM。比起传统的 Java 字节码操控框架,BCEL 或者 SERP,它具有更符合现代软件模式的编程模型和更迅捷的性能。
本文主要分为四个部分:首先将 ASM 和其他 Java 类生成方案作对比,然后大致介绍 Java 类文件的组织,最后针对最新的 ASM 3.0,描述其编程框架,并给出一个使用 ASM 进行 AOP 的例子,介绍调整函数内容,生成派生类,以及静态和动态生成类的方法。
引言
什么是 ASM ?
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
与 BCEL 和 SERL 不同,ASM 提供了更为现代的编程模型。对于 ASM 来说,Java class 被描述为一棵树;使用 “Visitor” 模式遍历整个二进制结构;事件驱动的处理方式使得用户只需要关注于对其编程有意义的部分,而不必了解 Java 类文件格式的所有细节:ASM 框架提供了默认的 “response taker”处理这一切。
为什么要动态生成 Java 类?
动态生成 Java 类与 AOP 密切相关的。AOP 的初衷在于软件设计世界中存在这么一类代码,零散而又耦合:零散是由于一些公有的功能(诸如著名的 log 例子)分散在所有模块之中;同时改变 log 功能又会影响到所有的模块。出现这样的缺陷,很大程度上是由于传统的 面向对象编程注重以继承关系为代表的“纵向”关系,而对于拥有相同功能或者说方面 (Aspect)的模块之间的“横向”关系不能很好地表达。例如,目前有一个既有的银行管理系统,包括 Bank、Customer、Account、Invoice 等对象,现在要加入一个安全检查模块, 对已有类的所有操作之前都必须进行一次安全检查。
图 1. ASM – AOP
然而 Bank、Customer、Account、Invoice 是代表不同的事务,派生自不同的父类,很难在高层上加入关于 Security Checker 的共有功能。对于没有多继承的 Java 来说,更是如此。传统的解决方案是使用 Decorator 模式,它可以在一定程度上改善耦合,而功能仍旧是分散的 —— 每个需要 Security Checker 的类都必须要派生一个 Decorator,每个需要 Security Checker 的方法都要被包装(wrap)。下面我们以Account
类为例看一下 Decorator:
首先,我们有一个 SecurityChecker
类,其静态方法 checkSecurity
执行安全检查功能:
public class SecurityChecker { public static void checkSecurity() { System.out.println("SecurityChecker.checkSecurity ..."); //TODO real security check } }
另一个是 Account
类:
public class Account { public void operation() { System.out.println("operation..."); //TODO real operation } }
若想对 operation
加入对 SecurityCheck.checkSecurity()
调用,标准的 Decorator 需要先定义一个Account
类的接口:
public interface Account { void operation(); }
然后把原来的 Account
类定义为一个实现类:
public class AccountImpl extends Account{ public void operation() { System.out.println("operation..."); //TODO real operation } }
定义一个 Account
类的 Decorator,并包装