字节码增强技术
个人理解,是在Java字节码生成之后,运行期对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。这类增强字节码的技术基本都应用在减少冗余和屏蔽实现细节上,Java的字节代码的增强在很多的Java框架中都可以看到,比如Spring使用cglib对字节码的增强实现面向切面的编程AOP和自动代理类的生成等上。
字节码操作框架
- JDK的动态代理(利用JAVA反射)
- ASM:一个JAVA字节码分析、创建和修改的开源应用框架
- AspectJ:意思是Java的AspectJava的AOP,提供了强大的AOP功能
- CGLIB:功能强大,高性能的代码生成包,使用字节码处理框架ASM(对ASM的封装),来转换字节码并生成新的类
- Javassist:一个开源的分析、编辑和创建Java字节码的类库,在性能上Javassist高于反射,但低于ASM
补充:
CGLIB它为没有实现接口的类提供代理
,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择
字节码增强步骤
- 在内存中获取到原始的字节码,然后通用一些开源提供的API来修改它的byte[]数组,得到一个新的byte[]。(ASM,javassist,cglib等技术)
- 将这个新的数组写到PermGen区域,也就是加载它或替换原来的Class字节码(也可以在进程外部调用完成)
实现机制:一种是通过创建原始类的一个子类,也就是动态创建的这个类继承原来的类,现在的SpringAOP正式通过这种方式实现,另一种是非常暴力的,即直接修改原先的Class字节码,在许多类的跟踪过程中会用到这技术(类加载时修改字节码信息,运行时修改)
为什么要动态生成 Java 类?
动态生成 Java 类与 AOP 是密切相关的。AOP 的初衷在于软件设计世界中存在这么一类代码,零散而又耦合:零散是由于一些公有的功能(诸如著名的 log 例子)分散在所有模块之中;同时改变 log 功能又会影响到所有的模块。出现这样的缺陷,很大程度上是由于传统的 面向对象编程注重以继承关系为代表的“纵向”关系,而对于拥有相同功能或者说方面 (Aspect)的模块之间的“横向”关系不能很好地表达。
动态改变 Java 类就是要解决 AOP 的问题,提供一种得到系统支持的可编程的方法,自动化地生成或者增强 Java 代码。这种技术已经广泛应用于最新的 Java 框架内,如 Hibernate,Spring 等
AOP编程:CGLIB动态字节码生成
使用动态字节码生成技术实现AOP原理是在运行期间目标字节码加载后,生成目标类的子类,将切面逻辑加入到子类中,所以cglib实现AOP不需要基于接口
一个普通类,没有接口实现
/**
* 这是一个没有实现接口的实现类
*/
public class SystemLogImpl {
public void saveLog() {
System.out.println("记录日志");
}
public void filterLog() {
System.out.println("过滤日志");
}
}
使用Cglib动态代理
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 使用Cglib动态代理
*/
public class SystemLogCglib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstacne(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
if (arg1.getName().equals("filterLog")) {
System.out.println("当前方法为: filterLog方法");
}
arg3.invokeSuper(arg0, arg2);
return null;
}
}
测试AOP
public static void main(String[] args) {
SystemLogCglib cglib = new SystemLogCglib();
SystemLogImpl systemLog = (SystemLogImpl) cglib.getInstacne(new SystemLogImpl());
systemLog.filterLog();
systemLog.saveLog();
}
输出
当前方法为: filterLog方法
过滤日志
记录日志