博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用 Cglib 实现多重代理
阅读量:6629 次
发布时间:2019-06-25

本文共 5902 字,大约阅读时间需要 19 分钟。

前言

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

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)

......省略代码        // 以下部分的字节码,每次生成 Proxy 实例都会插入。JVM 验证字节码时则会报错。        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 出来的字节码查看则更为直观:

1240

1240

生成的字节码中,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 方法是实现多重代理的核心。

每次调用目标方法,都会根据目标方法,和目标方法的多个拦截点生成一个调用对象。

// 生成调用对象CglibMethodInvocation c = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);// 调用 c.proceed();

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

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 {                // Skip this interceptor and invoke the next in the chain. 递归.                return proceed();            }        }        else {            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);        }    }

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

看下他的 invoke 方法:

public Object invoke(MethodInvocation mi) throws Throwable {        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;       // AOP 里熟悉的 ProceedingJoinPoint 参数!!!!        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 图:

1315310-20181005085323836-1214005954.png

代码如下:

Test.java & SayHello.java

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

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
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

public class Chain {    private List
list; private int index = -1; private Object target; public Chain(List
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

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 结果:

1240

符合预期。

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

你可能感兴趣的文章
图表控件Edraw Max免费下载地址
查看>>
Citrix XenApp应用虚拟化之RTX串号问题解决
查看>>
我的友情链接
查看>>
03-k8s-master
查看>>
我的友情链接
查看>>
Linux Centos的Inode及Block相关知识
查看>>
Zabbix server 搭建
查看>>
ehcache实例
查看>>
《ERP项目管理散记》第二章(科学估算—— 应用PERT估计项目工期)
查看>>
Code::Blocks设定相关
查看>>
PL/SQL基础篇10(trigger)
查看>>
Spring Security权限框架理论与简单Case
查看>>
Invalid icc profile: duplicate sequence numbers
查看>>
day1
查看>>
开源中国的 IT 公司开源软件整理计划介绍
查看>>
DDoS deflate - Linux下防御/减轻DDOS***
查看>>
关于outlook2007在WIN7下收取邮件很慢的解决办法
查看>>
Android第三十七天
查看>>
VMM2012应用指南之13- 自助服务门户网站的使用
查看>>
GraphQL入门
查看>>