字节码增强和spring AOP 原理

字节码增强和spring AOP 原理

————AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术。

两种实现机制:一种是通过创建原始类的一个子类,现在的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中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

JDK API 内置 ---- 通过 Proxy类,为目标对象创建代理 (必须面向接口代理 )

public class JdkProxyFactory implements InvocationHandler {
	// 被代理对象
	private Object target;
 
	// 在构造方法对象时,传入被代理对象
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
 
	// 创建代理
	public Object createProxy() {
		// 三个参数: 类加载器、 实现接口、 invocationhandler
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("记录日志!!!!!!");
		// 调用目标真实方法
		// target 被代理对象, args 方法参数 , method 被调用的方法
		return method.invoke(target, args);
	}
}

缺点: 使用Jdk动态代理,必须要求target目标对象,实现接口 ,如果没有接口,不能使用Jdk动态代理 。

二、使用CGLib实现:

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

1 什么是CGLIB?

CGLIB(Code Generation Library)是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择

2 CGLIB原理

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。

Cglib 不但可以对接口进行代理,也可以对目标类对象,实现代理(解决了 Jdk 只能对接口代理问题 )在spring3.2版本 core包中内置cglib 类 。

public class CglibProxyFactory implements MethodInterceptor {
	// 被代理目标对象
	private Object target;
 
	// 在构造工厂时传入被代理对象
	public CglibProxyFactory(Object target) {
		this.target = target;
	}
 
	// 创建代理对象方法
	public Object createProxy() {
		// 1、 创建Enhancer对象
		Enhancer enhancer = new Enhancer();
 
		// 2、 cglib创建代理,对目标对象,创建子类对象
		enhancer.setSuperclass(target.getClass());
 
		// 3、传入 callback对象,对目标增强
		enhancer.setCallback(this);
 
		return enhancer.create();
	}
 
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("记录日志......");
		// 按照JDK编程
		return method.invoke(target, args);
	}
}

Cglib 创建代理思想: 对目标类创建子类对象 :

  • 设置 superClass 对目标对象创建子类对象 (类似 JDK代理 接口)
  • 设置 callback 实现增强代码 (类似 JDK代理 InvocationHandler )
  • 在cglib的callback函数中,要执行被代理对象的方法 method.invoke(target, args); 等价于methodProxy.invokeSuper(proxy, args);

注意:

  • 优先对接口代理 (使用JDK代理),如果目标没有接口,才会使用cglib代理 !
  • Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向织入
    (1)若目标对象实现了若干接口,Spring使用JDK的java.lang.reflect.Proxy类代理。
    (2)若目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。
  • 程序中应优先对接口创建代理,便于程序解耦维护。
  • 标记位final的方法,不能被代理,因为无法进行覆盖:
  • JDK动态代理,是针对接口生产成子类,接口中方法不能使用final修饰;
  • CGLib是针对目标类生产子类,因此类或方法不能是final的;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值