日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一次动态代理的填坑之旅

發布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一次动态代理的填坑之旅 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自??一次動態代理的填坑之旅

背景

想在現有的接口加上熔斷降級或者限流的功能,比較好的方式是通過注解的方式,并基于動態代理進行實現,下面代碼是Rhino的實現

@Rhino public class ServiceImpl {@Degrade(rhinoKey = "syncMethod-0", ?fallBackMethod = "fallbackMethod")public void method() throws Exception {int i = 1 / 0;}private String fallbackMethod() throws Exception {return "fallback";}}

通過在方法添加@Degrade注解,很方便的賦予了method方法熔斷降級功能,在該方法的失敗率達到閾值時,就自動熔斷,并調用降級方法。

這里的動態代理并沒有使用Spring的AOP,而是自己實現了 BeanPostProcessor和 BeanFactoryPostProcessor接口,另外也實現了 PriorityOrdered接口。

在生成動態代理對象的時候,根據類是否有實現接口,選擇使用JDK的Proxy還是使用Cglib。

/** * @param clazz * @param origin * @return */private static Object createProxyService(Class clazz, Object origin) {Object proxy;if (clazz.getInterfaces().length > 0) {proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new RhinoInvocationHandler(origin, clazz));} else {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(new RhinoInvocationHandler(origin, clazz));proxy = enhancer.create();}return proxy; }

?

這樣的實現,在99%的情況下,是沒有問題的,直到有一天,有兩個業務同時反饋了一個問題,使用了熔斷降級的注解之后,發現自身的Spring AOP注解失效了或者是直接啟動異常,WTF,這個問題一直都沒考慮過好嗎...

為什么會這樣?Spring AOP到底干了什么,或者是自己的注解到底有什么問題?

復現問題

為了快速定位問題,并解決問題,最好的辦法就是復現該問題,寫了一個簡單的Spring AOP的例子。

@Component ?//加入到IoC容器 @Aspect ?//指定當前類為切面類 public class Aop {@Pointcut("execution(* com.dianping.rhino.aop.*.*(..))")public void pointCut(){}@Before("pointCut()")public void begin(){System.out.println("begin");}@After("pointCut()")public void close(){System.out.println("close");}@Around(value = "@annotation(MethodLog)")public void around() {System.out.println(" MethodLog ");} }

這里簡短的解釋下Spring AOP各個注解的作用 @Aspect切面,標識該類是一個切面類 @Pointcut切入點,用來標識哪些方法是需要被添加切面的 @Before在切入點,執行方法之前進行增強 @After在切入點,執行方法之后進行增強 @Around在該例子中,只有添加了@MethodLog注解的方法才會被增強

一切準備就緒,開啟DEBUG之旅,Spring的內部邏輯有點復雜,整個過程需要一點耐心。

?

通過Debug發現,Rhino的代理對象Processor排在Spring AOP的Processor,意味著Rhino生成的代理對象,會傳給Spring AOP的Processor再做一層代理,在Spring AOP生成代理對象的內部邏輯中,有這么一段判斷邏輯。

?

在createProxy方法中,會根據傳入的beanClass,即上一個Processor處理過的對象,判斷是否有實現接口。

回到Rhino的實現,因為ServiceImpl類沒有實現接口,所以內部會采用CGLIB的方式創建代理對象,我們來看下這個對象的接口。

?

好家伙,默默的給加了一個Factory接口,這樣在Spring AOP的處理中就當做有接口的情況進行實現了。這里最大的問題是,最終生成的代理對象是Factory類型的對象,在賦值給ServiceImpl變量時就會拋異常了。

解決問題

終于找到了問題的所在,那么改如何解決呢? 無法修改Spring AOP的邏輯,但是可以控制Rhino的邏輯,只需要把Rhino的Processor移到Spring AOP的Processor之后,這樣就可以在Rhino的Proccessor中處理經過Spring AOP的代理過的beanClass對象了,有效的避免這個問題。

如何有效調整Processor的處理順序? 通過分析發現Spring AOP的內部實現基于Ordered接口,而Rhino的實現是基于 PriorityOrdered接口,而且處理器在初始化完成后會進行排序,實現 PriorityOrdered接口的會放到前面,相同類型的再根據設置的order進行排序。很顯然,Rhino的Processor被放在前面,找到了問題,解決方法也很簡單,Rhino也換成Order接口,并且order設置成 LOWEST_PRECEDENCE,即排在最后面。

結論

經過上面的調整之前,這個問題確實被有效的解決了,以前遇到Spring的異常,都是一臉懵逼。所以,遇到類似的問題,最好通過DEBUG源碼去發現問題,并解決問題,這樣可以有效的防止后續的繼續挖坑。

總結

以上是生活随笔為你收集整理的一次动态代理的填坑之旅的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。