当前位置:网站首页>Cglib 如何实现多重代理?
Cglib 如何实现多重代理?
2020-11-06 21:04:00 【Java技术栈】
由于 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 出来的字节码查看则更为直观:
生成的字节码中,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 图 :
代码如下:
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<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
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
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 结果:
符合预期。
作者:莫那·鲁道
来源:https://www.cnblogs.com/stateis0/p/9744123.html 近期热文推荐:
1.Java 15 正式发布, 14 个新特性,刷新你的认知!!
2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!
3.我用 Java 8 写了一段逻辑,同事直呼看不懂,你试试看。。
觉得不错,别忘了随手点赞+转发哦!
版权声明
本文为[Java技术栈]所创,转载请带上原文链接,感谢
https://my.oschina.net/javaroad/blog/4703302
边栏推荐
- Did you blog today?
- 每个前端工程师都应该懂的前端性能优化总结:
- Multi classification of unbalanced text using AWS sagemaker blazingtext
- Natural language processing - wrong word recognition (based on Python) kenlm, pycorrector
- With the advent of tensorflow 2.0, can pytoch still shake the status of big brother?
- Python + appium automatic operation wechat is enough
- How to become a data scientist? - kdnuggets
- [JMeter] two ways to realize interface Association: regular representation extractor and JSON extractor
- Vite + TS quickly build vue3 project and introduce related features
- Tool class under JUC package, its name is locksupport! Did you make it?
猜你喜欢
TensorFlow中的Tensor是什么?
Interface pressure test: installation, use and instruction of siege pressure test
一篇文章带你了解SVG 渐变知识
The road of C + + Learning: from introduction to mastery
Using NLP and ml to extract and construct web data
至联云分享:IPFS/Filecoin值不值得投资?
速看!互联网、电商离线大数据分析最佳实践!(附网盘链接)
What to do if you are squeezed by old programmers? I don't want to quit
Word segmentation, naming subject recognition, part of speech and grammatical analysis in natural language processing
前端工程师需要懂的前端面试题(c s s方面)总结(二)
随机推荐
Recommendation system based on deep learning
关于Kubernetes 与 OAM 构建统一、标准化的应用管理平台知识!(附网盘链接)
百万年薪,国内工作6年的前辈想和你分享这四点
Architecture article collection
至联云解析:IPFS/Filecoin挖矿为什么这么难?
前端基础牢记的一些操作-Github仓库管理
一篇文章教会你使用HTML5 SVG 标签
一篇文章教会你使用Python网络爬虫下载酷狗音乐
A course on word embedding
I think it is necessary to write a general idempotent component
Classical dynamic programming: complete knapsack problem
From zero learning artificial intelligence, open the road of career planning!
如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能
Installing the consult cluster
Let the front-end siege division develop independently from the back-end: Mock.js
The road of C + + Learning: from introduction to mastery
Interface pressure test: installation, use and instruction of siege pressure test
Brief introduction of TF flags
The difference between gbdt and XGB, and the mathematical derivation of gradient descent method and Newton method
6.6.1 localeresolver internationalization parser (1) (in-depth analysis of SSM and project practice)