使用 Cglib ,Javassist 实现多重代理 解决:Caused by: java.lang.ClassFormatError: Duplicate method name...

前言

由于 Cglib 本身的设计,无法实现在 Proxy 外面再包装一层 Proxy(JDK Proxy 可以),通常会报如下错误:

 
  1. Caused by: java.lang.ClassFormatError: Duplicate method name "newInstance" with signature "..........
  2.  
  3. at java.lang.ClassLoader.defineClass1(Native Method)
  4.  
  5. at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
  6.  
  7. ... 10 more

错误来源代码:

net.sf.cglib.proxy.Enhancer#generateClass(ClassVisitor v)

 
  1.       ......省略代码        // 以下部分的字节码,每次生成 Proxy 实例都会插入。JVM 验证字节码时则会报错。
  2.         if (useFactory || currentData != null) {            int[] keys = getCallbackKeys();
  3.             emitNewInstanceCallbacks(e);
  4.             emitNewInstanceCallback(e);
  5.             emitNewInstanceMultiarg(e, constructorInfo);
  6.             emitGetCallback(e, keys);
  7.             emitSetCallback(e, keys);
  8.             emitGetCallbacks(e);
  9.             emitSetCallbacks(e);
  10.         }

通过 dump 出来的字节码查看则更为直观:

1

2

 

生成的字节码中,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. CglibMethodInvocation c = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);
  3. // 调用 
  4. c.proceed();

然后调用父类 proceed 方法,其实就是一个过滤器模式:

 
  1.    public Object proceed() throws Throwable {
  2.         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  3.             return invokeJoinpoint();
  4.         }
  5.  
  6.         Object interceptorOrInterceptionAdvice =
  7.                 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  8.         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  9.             InterceptorAndDynamicMethodMatcher dm =
  10.                     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  11.             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
  12.                 return dm.interceptor.invoke(this);
  13.             }
  14.             else {
  15.                 // Skip this interceptor and invoke the next in the chain. 递归.
  16.                 return proceed();
  17.             }
  18.         }
  19.         else {
  20.             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  21.         }
  22.     }

 

注意最后一行,这里就是调用拦截点的 invoke 方法,这个拦截点的具体实现类:AspectJAroundAdvice。

看下他的 invoke 方法:

 
  1.     public Object invoke(MethodInvocation mi) throws Throwable {
  2. ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
  3.        // AOP 里熟悉的 ProceedingJoinPoint 参数!!!!
  4. ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
  5. JoinPointMatch jpm = getJoinPointMatch(pmi);
  6. return invokeAdviceMethod(pjp, jpm, null, null);
  7.     }

通常,我们在业务中编写 AOP 拦截代码时,都会接触到这个 ProceedingJoinPoint 参数,然后调用他的 proceed 方法调用目标方法。

这个 ProceedingJoinPoint 类的 proceed 方法最终会回调 DynamicAdvisedInterceptor 对的 proceed 方法。直到所有的拦截点全部执行完毕。最终执行目标类的方法。

所以,你设置的每个被拦截的方法,如果这个方法会被拦截多次,那么就会有多个 MethodInterceptor(不是 cglib 的)实例形成调用链。然后通过 ProceedingJoinPoint 传递给你拦截使用。

铺垫了这么多,我们自己来实现一个简单的,不能像 Spring 这么复杂!!!!

简单实现 Cglib 多重代理

先说一下思路:事实上很简单,只需要再拦截器里放一个过滤器链即可,用户在过滤器里拦截多重调用。这些拦截器,就像你加 @Around 注解的方法,只不过我们这里没有 Spring 那么方便而已。

画个 UML 图:

3

代码如下:

Test.java & SayHello.java

 
  1. public class Test {
  2.  
  3.     public static void main(String[] args) {
  4.         Object proxy = ProxyFactory.create().getProxy(new SayHello());
  5.         proxy.toString();
  6.     }
  7.  
  8.  
  9.     static class SayHello {
  10.  
  11.         @Override
  12.         public String toString() {
  13.             return "hello cglib !";
  14.         }
  15.     }
  16. }

ProxyFactory.java & Interceptor.java

 
  1. public class ProxyFactory {
  2.     private ProxyFactory() {}
  3.     public static ProxyFactory create() {
  4.         return new ProxyFactory();
  5.     }
  6.     public Object getProxy(Object origin) {
  7.         final Enhancer en = new Enhancer();
  8.         en.setSuperclass(origin.getClass());
  9.         List<Chain.Point> list = new ArrayList<>();
  10.         list.add(new Point1());
  11.         list.add(new Point2());
  12.         en.setCallback(new Interceptor(new Chain(list, origin)));
  13.         return en.create();
  14.     }
  15.     private class Interceptor
  16.         implements MethodInterceptor {
  17.         Chain chain;
  18.         public Interceptor(Chain chain) {
  19.             this.chain = chain;
  20.         }
  21.         @Override
  22.         public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
  23.             throws Throwable {
  24.             return chain.proceed();
  25.         }
  26.     }
  27. }

Chain.java & Point.java

 
  1. public class Chain {
  2.     private List<Point> list;
  3.     private int index = -1;
  4.     private Object target;
  5.  
  6.     public Chain(List<Point> list, Object target) {
  7.         this.list = list;
  8.         this.target = target;
  9.     }
  10.  
  11.     public Object proceed() {
  12.         Object result;
  13.         if (++index == list.size()) {
  14.             result = (target.toString());
  15.             System.err.println("Target Method invoke result : " + result);
  16.         } else {
  17.             Point point = list.get(index);
  18.             result = point.proceed(this);
  19.         }
  20.         return result;
  21.     }
  22.     interface Point {
  23.         Object proceed(Chain chain);
  24.     }
  25. }

Point1.java & Point2.java

 
  1. public class Point1 implements Chain.Point {    @Override
  2.     public Object proceed(Chain chain) {
  3.         System.out.println("point 1 before");
  4.         Sleep.sleep(20);
  5.         Object result = chain.proceed();
  6.         Sleep.sleep(20);
  7.         System.out.println("point 1 after");        return result;
  8.     }
  9. }public class Point2 implements Chain.Point {    @Override
  10.     public Object proceed(Chain chain) {
  11.         System.out.println("point 2 before");
  12.         Sleep.sleep(20);
  13.         Object result = chain.proceed();
  14.         Sleep.sleep(20);
  15.         System.out.println("point 2 after");        return result;
  16.     }
  17. }

运行 Test main 结果:

4

转载自:https://www.cnblogs.com/stateis0/p/9744123.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值