一、CGLIB 动态代理
静态代理和 JDK 动态代理模式有个相同的特点就是都是依靠接口实现的,然而并不是所有的类都有接口的,这个时候就用不了 JDK 动态代理了,CGLIB 动态代理就可以闪亮登场了
CGLIB 动态大力采用了非常底层的字节码技术,其原理是通过字节码技术在内存中为一个类创建子类,并在子类中采用方法拦截的方式拦截所有父类方法的调用,顺势织入横切逻辑,实现方法增强
二、CGLIB 动态代理实现步骤
- 查找 A 类上的所有非 final 的 public 类型的方法定义
- 将这些方法的定义转换成字节码
- 将组成的字节码转换成相应的代理的 class 对象
- 实现 MethodInterceptor 接口,用来处理对代理类上所有方法的请求
这个接口和 JDK 动态代理中的 InvocationHandler 接口功能和角色是一样的
三、实战演示
真实角色
public class UserServiceImpl {
public void select() {
System.out.println("查询了一个用户");
}
}
代理类
public class Proxy implements MethodInterceptor {
private Object target; // 聚合一个目标对象(选中 alt+enter)
/**
* 传入一个被代理的对象
*/
public Proxy(Object target) {
this.target = target;
}
// 返回一个代理对象,是target对象的代理对象
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer(); // 创建加强器,用来创建动态代理类
enhancer.setSuperclass(target.getClass()); // 为代理类指定需要代理的类,即设置父类
// 可在这里添加拦截器
/**
* 设置回调函数
* 这里相当于是对于代理类上所有方法的调用,都会调用 Callback,而 Callback 则需要实现intercept() 方法进行拦截
*/
enhancer.setCallback(this);
return enhancer.create(); // 创建子类对象,即代理对象
}
/**
* @param o 代表 CGLIB 生成的动态代理类,对象本身
* @param method 代理类中被拦截的接口方法,Method 实例
* @param objects 接口方法参数
* @param methodProxy 用于调用父类真正的业务类方法。可以直接调用被代理类接口方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---------- CGLIB 动态代理模式开始");
log("执行了" + method.getName());
Object returnVal = method.invoke(target, objects);
System.out.println("---------- CGLIB 动态代理模式提交");
return returnVal;
}
public void log(String msg) {
System.out.println("[Debug] --> 使用了" + msg + "方法");
}
}
客户端
public class Client {
public static void main(String[] args) {
UserServiceImpl usi = new UserServiceImpl(); // 创建目标对象
// 获取到代理对象,并且将目标对象传递给代理对象
UserServiceImp proxyInstance = (UserServiceImp) new Proxy(usi).getProxyInstance();
// 执行代理对象的方法,触发intecept方法,从而实现对目标对象的调用
proxyInstance.select();
}
}
四、CGLIB 动态代理总结
- CGLIB 可以传接口也可以传类,接口使用实现的方式,类则使用继承的方式
- CGLIB 不能代理 static、private、final 修饰的方法,因为此代理采用的是继承
- 代理是通过反射技术来调用,实际上在这个层面做了方法访问的优化
它使用建立方法索引的方式避免了传统 JDK 动态代理需要通过 Method 方法反射调用
它提供了callback 设计,可以灵活的给不同的方法绑定不同的callback,编码方便 - CGLIB 会默认代理 Object 中 equals()、hashCode()、toString()、clone(),JDK 则没有 clone 方法