一、CGlib代理原理
在面向对象编程中,代理模式是一种常用的设计模式。它可以在不改变原有对象代码的前提下,通过新增代理对象,在代理对象中增加一些其他的功能,从而提供更加完整、更加复杂的功能。CGlib代理是基于动态代理的一种实现方式,是Java中常用的代理技术之一。
动态代理和静态代理最大的区别就在于代理类的生成方式。静态代理需要人工编写代理类,而动态代理则可以在运行时动态生成代理类。Java JDK提供了一个Proxy类,可以通过Proxy的工厂方法newProxyInstance动态生成代理对象。
不过JDK提供的动态代理有一个限制,就是只能代理实现了接口的类。如果要代理没有实现接口的类,就要使用其他的代理技术。而CGlib是一种可以代理类的技术。使用CGlib实现动态代理,本质上是在运行时,通过字节码处理框架ASM,动态生成一个被代理类的子类,并覆盖其中的方法,达到代理的目的。
二、CGlib原理
CGlib,全称为Code Generation Library,是一个开源的非侵入式的Java字节码生成库。它允许在运行时对字节码进行操作,生成新的类,以实现例如AOP之类的功能。
与静态代理和JDK动态代理不同,CGlib在运行时动态生成被代理类的子类,并重写其中的方法。因此,对被代理类的方法访问是通过子类方法访问的,而不是被代理类方法访问的。子类方法可以通过调用父类方法实现对被代理类方法的访问。
CGlib生成子代理类的方式是在内存中构建代理类的所有方法的字节码,并将其转换成class对象,从而实现对被代理类方法的代理。由于CGlib是直接操作class字节码实现代理的,因此需要引入ASM字节码框架,用于处理Java字节码。
三、CGlib底层原理
CGlib底层运用Java字节码技术实现代理,下面简单介绍一下Java字节码的执行过程:
一是Java程序源代码被编译成Java字节码文件(.class文件)。
二是Java虚拟机(JVM)将Java字节码文件解释成机器指令。
三是CPU执行机器指令,进行计算操作。
CGlib底层实现通过ASM技术直接操作字节码来完成对代理对象的生成,主要分为以下几个步骤:
1、确定需要代理的类和方法,生成对应的MethodInterceptor实现类。
public class UserProxy implements MethodInterceptor { private Object target; public UserProxy(Object target) { this.target = target; } public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method invoke"); Object result = method.invoke(target, args); System.out.println("After method invoke"); return result; } }
2、使用ASM库加载被代理类的字节码,并动态生成代理对象的子类。
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new UserProxy(target)); Object proxy = enhancer.create();
在这个过程中,我们需要在内存中先读取被代理类的二进制字节码文件,然后再解析出类信息,这个时候我们就要使用ASM库:
ClassReader classReader = new ClassReader(target.getClass().getName()); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor classVisitor = new AddFieldAdapter(classWriter, Opcodes.ACC_PUBLIC, "hash", "I"); classReader.accept(classVisitor, ClassReader.SKIP_DEBUG); byte[] data = classWriter.toByteArray();
四、CGlib底层实现原理
底层实现原理主要是通过ASM(a very small and fast Java bytecode manipulation framework)框架来直接操作数据字节码。ASM可以直接生成和修改字节码,也可以在字节码的生成和修改中提供一些帮助和支持。
ASM使用Visitor模式来操作字节码,它通过ClassVisitor和MethodVisitor类提供了访问类层次结构和方法级别元素的途径。
CGlib实现动态代理的过程如下:
1、创建Enhancer对象。
2、设置父类和回调方法。
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new UserServiceInterceptor());
3、通过Enhancer对象的create()方法生成代理对象。
UserServiceImpl proxyUserService = (UserServiceImpl) enhancer.create();
4、调用代理对象方法。
String username = userSerivceimpl.getUserName();
五、CGlib动态代理原理
CGlib可以创建一个类的子类,并且在子类中采用方法拦截的技术拦截所有父类方法的调用。在拦截器中可以添加一些额外的逻辑。相比Java动态代理,CGlib不要求被代理类必须实现接口,它是基于类代理,对指定的类生成一个子类,并覆盖其中的方法来实现代理的功能。
在使用CGlib进行代理时,代理类和被代理类构成了继承关系。代理类继承被代理类,所以代理类拥有了被代理类的所有属性和方法,而且可以拦截被代理方法进行增强。
下面是一个CGlib动态代理实现的示例代码:
public class UserServiceInterceptor implements MethodInterceptor { private UserService userService; public UserServiceInterceptor(UserService userService) { this.userService = userService; } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("begin method..."); Object result = method.invoke(userService, objects); System.out.println("end method..."); return result; } } Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new UserServiceInterceptor(userService)); UserService userServiceProxy = (UserService) enhancer.create();
结束语
CGlib是一种非常常用的代理方式,通过字节码的修改,可以实现对被代理对象的增强和功能扩展。在使用CGlib进行代理时,我们需要深入了解其实现原理,灵活运用其底层知识,才能更好地发挥其代理作用。