自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一個Spring AOP的坑!很多人都犯過!

開發(fā) 開發(fā)工具
很多讀者看完之后表示用起來很爽,但是后臺也有人留言說自己配置了Spring的AOP之后,發(fā)現(xiàn)切面不生效。其實,這個問題我在用的過程中也遇到過,而且還是同一個問題一天之內(nèi)遇到了兩次。

 [[352384]]

前幾天,我剛剛發(fā)布過一篇文章《自定義注解!絕對是程序員裝逼的利器!!》,介紹過如何使用Spring AOP + 自定義注解來提升代碼的優(yōu)雅性。

很多讀者看完之后表示用起來很爽,但是后臺也有人留言說自己配置了Spring的AOP之后,發(fā)現(xiàn)切面不生效。

其實,這個問題我在用的過程中也遇到過,而且還是同一個問題一天之內(nèi)遇到了兩次。

說明這個問題很容易被忽略,并且這個問題帶來的后果可能是極其嚴重的。那么,我們就來簡單回顧一下問題是怎么樣的。

問題重現(xiàn)

最初我定義了一個注解,希望可以方便統(tǒng)一的對一些數(shù)據(jù)庫操作做緩存。于是就有了以下代碼:

首先,定義一個注解:

  1. @Target(ElementType.METHOD) 
  2.  
  3. @Retention(RetentionPolicy.RUNTIME) 
  4.  
  5. public @interface Cacheable { 
  6.  
  7.     /** 
  8.  
  9.      * 策略名稱,需要保證唯一 
  10.  
  11.      * @return 
  12.  
  13.      */ 
  14.  
  15.     public String keyName(); 
  16.  
  17.     /** 
  18.  
  19.      * 超時時長,單位:秒 
  20.  
  21.      * @return 
  22.  
  23.      */ 
  24.  
  25.     public int expireTime(); 
  26.  

然后自定義一個切面,對所有使用了該注解的方法進行切面處理:

  1. @Aspect 
  2.  
  3. @Component 
  4.  
  5. public class StrategyCacheAspect { 
  6.  
  7.     private static final Logger LOGGER = LoggerFactory.getLogger(FacadeAspect.class); 
  8.  
  9.     @Around("@annotation(com.hollis.cache.StrategyCache)"
  10.  
  11.     public Object cache(ProceedingJoinPoint pjp) throws Throwable { 
  12.  
  13.         // 先查緩存,如果緩存中有值,直接返回。如果緩存中沒有,先執(zhí)行方法,再將返回值存儲到緩存中。 
  14.  
  15.     } 
  16.  

然后就可以使用該注解了,使用方法如下:

  1. @Component 
  2.  
  3. public class StrategyService extends BaseStrategyService  { 
  4.  
  5.     public PricingResponse getFactor(Map<String, String> pricingParams) { 
  6.  
  7.         // 做一些參數(shù)校驗,以及異常捕獲相關的事情 
  8.  
  9.         return this.loadFactor(tieredPricingParams); 
  10.  
  11.     } 
  12.  
  13.     @Override 
  14.  
  15.     @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2) 
  16.  
  17.     private PricingResponse loadFactor(Map<String, String> pricingParams) { 
  18.  
  19.         //代碼執(zhí)行 
  20.  
  21.     } 
  22.  

以上,對loadFactor方法增加了切面,為了方便使用,我們還定義了一個getFactor方法,設置為public,方便外部調(diào)用。

但是,在調(diào)試過程中,我發(fā)現(xiàn)我們設置在loadFactor方法上面的切面并沒有成功,無法執(zhí)行切面類。

于是開始排查問題具體是什么。

問題排查

為了排查這個問題,首先是把所有的代碼檢查一遍,看看切面的代碼是不是有問題,有沒有可能有手誤打錯了字之類的。

但是發(fā)現(xiàn)都沒有。于是就想辦法找找問題。

接下來我把loadFactor的訪問權限從private改成public,發(fā)現(xiàn)沒有效果。

然后我嘗試著在方法外直接調(diào)用loadFactor而不是getFactor。

發(fā)現(xiàn)這樣做就可以成功的執(zhí)行到切面里面了。

發(fā)現(xiàn)這一現(xiàn)象的時候,我突然恍然大悟,直捶大腿。原來如此,原來如此,就應該是這樣的。

我突然就想到了問題的原因。其實原因挺簡單的,也是我之前了解到過的原理,但是在問題剛剛發(fā)生的時候我并沒有想到這里,而是通過debug,發(fā)現(xiàn)這個現(xiàn)象之后我才突然想到這個原理。

那么,就來說說為什么會發(fā)生這樣的問題。

代理的調(diào)用方式

我們發(fā)現(xiàn)上面的問題關鍵在于loadFactor方法被調(diào)用的方式不同。我們知道,方法的調(diào)用通常有以下幾種方式:

1、在類內(nèi)部,通過this進行自調(diào)用:

  1. public class SimplePojo implements Pojo { 
  2.  
  3.     public void foo() { 
  4.  
  5.         // this next method invocation is a direct call on the 'this' reference 
  6.  
  7.         this.bar(); 
  8.  
  9.     } 
  10.  
  11.     public void bar() { 
  12.  
  13.         // some logic... 
  14.  
  15.     } 
  16.  

2、在類外部,通過該類的對象進行調(diào)用

  1. public class Main { 
  2.  
  3.     public static void main(String[] args) { 
  4.  
  5.         Pojo pojo = new SimplePojo(); 
  6.  
  7.         // this is a direct method call on the 'pojo' reference 
  8.  
  9.         pojo.foo(); 
  10.  
  11.     } 
  12.  

類關系及調(diào)用過程中如下圖:

如果是靜態(tài)方法,也可以通過類直接調(diào)用。

3、在類外部,通過該類的代理對象進行調(diào)用:

  1. public class Main { 
  2.  
  3.     public static void main(String[] args) { 
  4.  
  5.         ProxyFactory factory = new ProxyFactory(new SimplePojo()); 
  6.  
  7.         factory.addInterface(Pojo.class); 
  8.  
  9.         factory.addAdvice(new RetryAdvice()); 
  10.  
  11.         Pojo pojo = (Pojo) factory.getProxy(); 
  12.  
  13.         // this is a method call on the proxy! 
  14.  
  15.         pojo.foo(); 
  16.  
  17.     } 
  18.  

類關系及調(diào)用過程中如下圖:

那么,Spring的AOP其實是第三種調(diào)用方式,就是通過代理對象調(diào)用,只有這種調(diào)用方式,才能夠在真正的對象的執(zhí)行前后,能夠讓代理對象也執(zhí)行相關代碼,才能起到切面的作用。

而對于使用this的方式調(diào)用,這種只是自調(diào)用,并不會使用代理對象進行調(diào)用,也就無法執(zhí)行切面類。

問題解決

那么,我們知道了,想要真正的執(zhí)行代理,那么就需要通過代理對象進行調(diào)用而不是使用this調(diào)用的方式。

那么,這個問題的解決辦法也就是想辦法通過代理對象來調(diào)用目標方法即可。

這種問題的解決網(wǎng)上有很多種辦法,這里介紹一個相對簡單的。其他的更多的辦法大家可以在網(wǎng)上找到一些案例。搜索關鍵詞"AOP 自調(diào)用"即可。

獲取代理對象進行調(diào)用

我們需要修改一下前面的StrategyService的代碼,修改成以下內(nèi)容:

  1. @Component 
  2.  
  3. public class StrategyService{ 
  4.  
  5.     public PricingResponse getFactor(Map<String, String> pricingParams) { 
  6.  
  7.         // 做一些參數(shù)校驗,以及異常捕獲相關的事情 
  8.  
  9.         // 這里不使用this.loadFactor而是使用AopContext.currentProxy()調(diào)用,目的是解決AOP代理不支持方法自調(diào)用的問題 
  10.  
  11.         if (AopContext.currentProxy() instanceof StrategyService) { 
  12.  
  13.             return ((StrategyService)AopContext.currentProxy()).loadFactor(tieredPricingParams); 
  14.  
  15.         } else { 
  16.  
  17.             // 部分實現(xiàn)沒有被代理過,則直接進行自調(diào)用即可 
  18.  
  19.             return loadFactor(tieredPricingParams); 
  20.  
  21.         } 
  22.  
  23.     } 
  24.  
  25.     @Override 
  26.  
  27.     @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2) 
  28.  
  29.     private PricingResponse loadFactor(Map<String, String> oricingParams) { 
  30.  
  31.         //代碼執(zhí)行 
  32.  
  33.     } 
  34.  

即使用AopContext.currentProxy()獲取到代理對象,然后通過代理對象調(diào)用對應的方法。

還有個地方需要注意,以上方式還需要將Aspect的expose-proxy設置成true。如果是配置

文件修改:

  1. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/> 

如果是SpringBoot,則修改應用啟動入口類的注解:

  1. @EnableAspectJAutoProxy(exposeProxy = true
  2.  
  3. public class Application { 
  4.  

總結

以上,我們分析并解決了一個Spring AOP不支持方法自調(diào)用的問題。

AOP失敗這個問題,其實還是很嚴重的,因為如果發(fā)生非預期的失效,那么直接問題就是沒有執(zhí)行切面方法,更嚴重的后果可能是諸如事務未生效、日志未打印、緩存未查詢等各種問題。

所以,還是建議大家看完此文之后,統(tǒng)查一下自己的代碼,是否存在方法自調(diào)用的情況。這種情況下,任何切面都是無法生效的!

【本文是51CTO專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】

戳這里,看該作者更多好文

 

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2020-02-28 10:10:07

開源技術 工具

2021-04-04 23:19:37

5G4G技術

2017-12-04 01:08:15

戴爾

2019-08-08 16:00:08

HTTPGETPOST

2014-05-09 15:29:21

2020-07-14 08:43:54

VueHTML函數(shù)

2020-06-04 18:34:15

路由器耗電硬件

2024-09-05 09:30:51

FunctionJava8接口

2024-08-19 02:00:00

FunctionJava8接口

2024-09-12 08:32:42

2021-01-30 11:42:53

迭代器代碼元素

2025-02-21 08:48:16

Typescript內(nèi)置聯(lián)合類型

2022-12-05 15:23:33

JavaScript技巧運算符

2022-02-06 00:07:19

互聯(lián)網(wǎng)失業(yè)職業(yè)

2018-02-13 14:48:17

戴爾

2021-10-08 15:50:14

手機屏幕安卓

2022-04-19 08:50:43

Java8接口

2021-10-13 21:55:01

電腦軟件內(nèi)存

2019-12-09 10:02:41

泛型ypeScript前端

2022-07-06 10:33:39

技術債務CIO
點贊
收藏

51CTO技術棧公眾號