- Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。Java字节码增强的应用场景主要是减少冗余代码,对开发人员屏蔽底层的实现细节。 字节码增强技术主要有两种实现机制:一种是通过创建原始类的一个子类;另一种是很暴力的方式,直接去修改原先的class字节码。
- 字节码增强技术:AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术。
应用场景:某一天系统出现OOM,通过工具分析,是莫各类的对象占用了很大空间,但是这个对象被许多程序访问,那么就很难找到,工程的全文匹配也只能找到自己的业务代码调用的地方,深入的反射,三方包调用无法匹配。这个时候AOP就可以帮助完成。 - 两种实现机制:一种是通过创建原始类的一个子类,也就是动态创建的这个类继承原来的类,现在的SpringAOP正式通过这种方式实现,另一种是非常暴力的,即直接修改原先的Class字节码,在许多类的跟踪过程中会用到这技术(类加载时修改字节码信息,运行时修改)。
- 字节码增强步骤:
1.在内存中获取到原始的字节码,然后通用一些开源提供的API来修改它的byte[]数组,得到一个新的byte[]。(ASM,javassist,cglib等技术)
2.将这个新的数组写到PermGen区域,也就是加载它或替换原来的Class字节码(也可以在进程外部调用完成)
反射和ASM区别
反射是读取持久堆上存储的类信息。而 ASM 是直接处理 .class 字节码的小工具(工具虽小,但是功能非常强大!)
反射只能读取类信息,而 ASM 除了读还能写。
反射读取类信息时需要进行类加载处理,而 ASM 则不需要将类加载到内存中。
反射相对于 ASM 来说使用方便,想直接操纵 ASM 的话需要有 JVM 指令基础。
Spring AOP 代理实现有两种: JDK动态代理 和 Cglib框架动态代理
一、首先说一下JDK中的动态代理:
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,
但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
二、使用CGLib实现:
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
JDK动态代理 简单介绍
JDK API 内置 ---- 通过 Proxy类,为目标对象创建代理 (必须面向接口代理 )
[java] view plain copy
1. public class JdkProxyFactory implements InvocationHandler {
2. // 被代理对象
3. private Object target;
4.
5. // 在构造方法对象时,传入被代理对象
6. public JdkProxyFactory(Object target) {
7. this.target = target;
8. }
9.
10. // 创建代理
11. public Object createProxy() {
12. // 三个参数: 类加载器、 实现接口、 invocationhandler
13. return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
14. }
15.
16. @Override
17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
18. "记录日志!!!!!!");
19. // 调用目标真实方法
20. // target 被代理对象, args 方法参数 , method 被调用的方法
21. return method.invoke(target, args);
22. }
23. }
缺点: 使用Jdk动态代理,必须要求target目标对象,实现接口 ,如果没有接口,不能使用Jdk动态代理
Cglib 动态代理
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
一、什么是CGLIB?
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
二、CGLIB原理
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB缺点:对于final方法,无法进行代理。
Cglib 不但可以对接口进行代理,也可以对目标类对象,实现代理 (解决了 Jdk 只能对接口代理问题 )在spring3.2版本 core包中内置cglib 类
[java] view plain copy
1. public class CglibProxyFactory implements MethodInterceptor {
2. // 被代理目标对象
3. private Object target;
4.
5. // 在构造工厂时传入被代理对象
6. public CglibProxyFactory(Object target) {
7. this.target = target;
8. }
9.
10. // 创建代理对象方法
11. public Object createProxy() {
12. // 1、 创建Enhancer对象
13. new Enhancer();
14.
15. // 2、 cglib创建代理,对目标对象,创建子类对象
16. enhancer.setSuperclass(target.getClass());
17.
18. // 3、传入 callback对象,对目标增强
19. this);
20.
21. return enhancer.create();
22. }
23.
24. @Override
25. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
26. "记录日志......");
27. // 按照JDK编程
28. return method.invoke(target, args);
29. }
30. }
Cglib 创建代理思想: 对目标类创建子类对象
设置 superClass 对哪个类创建子类 (类似 JDK代理 接口)
设置 callback 实现增强代码 (类似 JDK代理 InvocationHandler )
在cglib的callback函数中,要执行被代理对象的方法
method.invoke(target, args); 等价于 methodProxy.invokeSuper(proxy, args);
优先对接口代理 (使用JDK代理),如果目标没有接口,才会使用cglib代理 !