由于 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 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/203300.html
標籤:其他
上一篇:『JVM』我不想知道我是怎么來滴,我就想知道我是怎么沒滴
下一篇:010_初建工程
