Cglib代理

使用

和 jdk 动态代理原理查不多
  1. 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
    1. notion image
  1. 调用目标时有所改进,见下面代码片段
    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化
    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
public class CglibProxyDemo { static class Target { public void foo() { System.out.println("target foo"); } } // 代理是子类型, 目标是父类型 public static void main(String[] param) { // Target target = new Target(); Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> { System.out.println("before..."); // Object result = method.invoke(target, args); // 用方法反射调用目标 // methodProxy 它可以避免反射调用 // Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring) Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理 System.out.println("after..."); return result; }); System.out.println(proxy.getClass()); proxy.foo(); } }
notion image
spring使用的methodProxy.invoke(target,args);
 
jad com.onethink.a11.CglibProxyDemo$Target$$EnhancerByCGLIB$$ffccea3c
 
notion image

final代理失效

final 修饰类或者方法无法增强,因为cglib是基于类继承的,final类没有子类,final方法无法重写
static class Target { public final void foo() { System.out.println("target foo"); } }
并没有生成代理子类的增强foo方法,调用的是Target 的foo方法
notion image
 

原理

仿写代理类实现

目标类
public class Target { public void save() { System.out.println("save()"); } public void save(int i) { System.out.println("save(int)"); } public void save(long j) { System.out.println("save(long)"); } }
 
public class Proxy Target { private MethodInterceptor methodInterceptor; public void setMethodInterceptor(MethodInterceptor methodInterceptor) { this.methodInterceptor = methodInterceptor; } static Method save0; static Method save1; static Method save2; static MethodProxy save0Proxy; static MethodProxy save1Proxy; static MethodProxy save2Proxy; static { try { save0 = Target.class.getMethod("save"); save1 = Target.class.getMethod("save", int.class); save2 = Target.class.getMethod("save", long.class); save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");//无参数 save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");//整形参数 save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");//长整型参数 } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法 public void saveSuper() { super.save(); } public void saveSuper(int i) { super.save(i); } public void saveSuper(long j) { super.save(j); } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法 @Override public void save() { try { methodInterceptor.intercept(this, save0, new Object[0], save0Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save(int i) { try { methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save(long j) { try { methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } }
public static void main(String[] args) { Proxy proxy = new Proxy(); Target target = new Target(); proxy.setMethodInterceptor(new MethodInterceptor() { @Override public Object intercept(Object p, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before..."); // return method.invoke(target, args); // 反射调用 // FastClass return methodProxy.invoke(target, args); // 内部无反射, 结合目标用 // return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用 } }); proxy.save(); proxy.save(1); proxy.save(2L); } }

cglib 避免反射调用

 
  1. 当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类,FastClass的子类,一个针对目标,一个针对代理类
      • ProxyFastClass 配合代理对象一起使用, 避免反射
      • TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
  1. TargetFastClass 记录了 Target 中方法与编号的对应关系
      • save(long) 编号 2
      • save(int) 编号 1
      • save() 编号 0
      • 首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号
      • 然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射
  1. ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法
      • saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)
      • saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)
      • saveSuper() 编号 0,不增强, 仅是调用 super.save()
      • 查找方式与 TargetFastClass 类似
  1. 为什么有这么麻烦的一套东西呢?
      • 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
      • 用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
notion image
仿写FastClass的实现
 

TargetFastClass

//避免反射调用的原理
使用字节码生成技术,生成代理类的时候,会维护每一个目标类的方法签名和编号的映射关系
在调用目标类方法的时候,会先根据方法签名获得编号,通过编号去调用对应方法
public class TargetFastClass { static Signature s0 = new Signature("save", "()V"); static Signature s1 = new Signature("save", "(I)V"); static Signature s2 = new Signature("save", "(J)V"); // 获取目标方法的编号 /* Target save() 0 save(int) 1 save(long) 2 signature 包括方法名字、参数返回值 */ public int getIndex(Signature signature) { if (s0.equals(signature)) { return 0; } else if (s1.equals(signature)) { return 1; } else if (s2.equals(signature)) { return 2; } return -1; } // 根据方法编号, 正常调用目标对象方法 public Object invoke(int index, Object target, Object[] args) { if (index == 0) { ((Target) target).save(); return null; } else if (index == 1) { ((Target) target).save((int) args[0]); return null; } else if (index == 2) { ((Target) target).save((long) args[0]); return null; } else { throw new RuntimeException("无此方法"); } } }
 
public static void main(String[] args) { TargetFastClass fastClass = new TargetFastClass(); int index = fastClass.getIndex(new Signature("save", "(I)V")); System.out.println(index); fastClass.invoke(index, new Target(), new Object[]{100}); }

ProxyFastClass

public class ProxyFastClass { static Signature s0 = new Signature("saveSuper", "()V"); static Signature s1 = new Signature("saveSuper", "(I)V"); static Signature s2 = new Signature("saveSuper", "(J)V"); // 获取代理方法的编号 /* Proxy saveSuper() 0 saveSuper(int) 1 saveSuper(long) 2 signature 包括方法名字、参数返回值 */ public int getIndex(Signature signature) { if (s0.equals(signature)) { return 0; } else if (s1.equals(signature)) { return 1; } else if (s2.equals(signature)) { return 2; } return -1; } // 根据方法编号, 正常调用目标对象方法 public Object invoke(int index, Object proxy, Object[] args) { if (index == 0) { ((Proxy) proxy).saveSuper(); return null; } else if (index == 1) { ((Proxy) proxy).saveSuper((int) args[0]); return null; } else if (index == 2) { ((Proxy) proxy).saveSuper((long) args[0]); return null; } else { throw new RuntimeException("无此方法"); } } }
public static void main(String[] args) { ProxyFastClass fastClass = new ProxyFastClass(); int index = fastClass.getIndex(new Signature("saveSuper", "()V")); System.out.println(index); fastClass.invoke(index, new Proxy(), new Object[0]); }
notion image