由于 Cglib 本身的设计,无法实现在 Proxy 外面再包装一层 Proxy(JDK Proxy 可以),通常会报如下错误:
1 2 3 4 5 6 7 8 Caused by: java.lang.ClassFormatError: Duplicate method name "newInstance" with signature ".......... at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ... 10 more
错误来源代码:
net.sf.cglib.proxy.Enhancer#generateClass(ClassVisitor v)
1 2 3 4 5 6 7 8 9 10 11 12 13 ......省略代码 if (useFactory || currentData != null ) { int [] keys = getCallbackKeys(); emitNewInstanceCallbacks(e); emitNewInstanceCallback(e); emitNewInstanceMultiarg(e, constructorInfo); emitGetCallback(e, keys); emitSetCallback(e, keys); emitGetCallbacks(e); emitSetCallbacks(e); }
通过 dump 出来的字节码查看则更为直观:
生成的字节码中,newInstance 方法是重复的。
dump 方法: System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
如何处理? 实现多重代理,有一种蹩脚的方法,例如 JDK 和 Cglib 组合使用。或者你直接使用 JDK 代理。但有时候,针对类的操作还行不通。
笔者参考 Spring 的做法,实现了一个简单的多重代理。
Spring 的场景是:一个目标方法被多个 AOP 拦截,此时就需要多重代理。
Spring 创建代理的代码位于 :org.springframework.aop.framework.CglibAopProxy#getProxy
Spring AOP 拦截器类:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
该类的 intercept 方法是实现多重代理的核心。
每次调用目标方法,都会根据目标方法,和目标方法的多个拦截点生成一个调用对象。
1 2 3 4 CglibMethodInvocation c = new CglibMethodInvocation (proxy, target, method, args, targetClass, chain, methodProxy);c.proceed();
然后调用父类 proceed 方法,其实就是一个过滤器模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public Object proceed () throws Throwable { if (this .currentInterceptorIndex == this .interceptorsAndDynamicMethodMatchers.size() - 1 ) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this .interceptorsAndDynamicMethodMatchers.get(++this .currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this .method, this .targetClass, this .arguments)) { return dm.interceptor.invoke(this ); } else { return proceed(); } } else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this ); } }
注意最后一行,这里就是调用拦截点的 invoke 方法,这个拦截点的具体实现类:AspectJAroundAdvice。
看下他的 invoke 方法:
1 2 3 4 5 6 7 public Object invoke (MethodInvocation mi) throws Throwable { ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null , null ); }
通常,我们在业务中编写 AOP 拦截代码时,都会接触到这个 ProceedingJoinPoint 参数,然后调用他的 proceed 方法调用目标方法。
这个 ProceedingJoinPoint 类的 proceed 方法最终会回调 DynamicAdvisedInterceptor 对的 proceed 方法。直到所有的拦截点全部执行完毕。最终执行目标类的方法。
所以,你设置的每个被拦截的方法,如果这个方法会被拦截多次,那么就会有多个 MethodInterceptor(不是 cglib 的)实例形成调用链。然后通过 ProceedingJoinPoint 传递给你拦截使用。
铺垫了这么多,我们自己来实现一个简单的,不能像 Spring 这么复杂!!!!
简单实现 Cglib 多重代理 先说一下思路:事实上很简单,只需要再拦截器里放一个过滤器链即可,用户在过滤器里拦截多重调用。这些拦截器,就像你加 @Around 注解的方法,只不过我们这里没有 Spring 那么方便而已。
画个 UML 图 :
代码如下:
Test.java & SayHello.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Object proxy = ProxyFactory.create().getProxy(new SayHello ()); proxy.toString(); } static class SayHello { @Override public String toString () { return "hello cglib !" ; } } }
ProxyFactory.java & Interceptor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class ProxyFactory { private ProxyFactory () {} public static ProxyFactory create () { return new ProxyFactory (); } public Object getProxy (Object origin) { final Enhancer en = new Enhancer (); en.setSuperclass(origin.getClass()); List<Chain.Point> list = new ArrayList <>(); list.add(new Point1 ()); list.add(new Point2 ()); en.setCallback(new Interceptor (new Chain (list, origin))); return en.create(); } private class Interceptor implements MethodInterceptor { Chain chain; public Interceptor (Chain chain) { this .chain = chain; } @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return chain.proceed(); } } }
Chain.java & Point.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Chain { private List<Point> list; private int index = -1 ; private Object target; public Chain (List<Point> list, Object target) { this .list = list; this .target = target; } public Object proceed () { Object result; if (++index == list.size()) { result = (target.toString()); System.err.println("Target Method invoke result : " + result); } else { Point point = list.get(index); result = point.proceed(this ); } return result; } interface Point { Object proceed (Chain chain) ; } }
Point1.java & Point2.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Point1 implements Chain .Point { @Override public Object proceed (Chain chain) { System.out.println("point 1 before" ); Sleep.sleep(20 ); Object result = chain.proceed(); Sleep.sleep(20 ); System.out.println("point 1 after" ); return result; } } public class Point2 implements Chain .Point { @Override public Object proceed (Chain chain) { System.out.println("point 2 before" ); Sleep.sleep(20 ); Object result = chain.proceed(); Sleep.sleep(20 ); System.out.println("point 2 after" ); return result; } }
运行 Test main 结果:
符合预期。