問題背景:
要添加@Transactional注解,使某功能可以原子操作。
接口調用結構:controller->processor(類似Service層)->flow(類似Manager層)
附:推薦的工程結構:
1. 請求處理:(Web層)控制轉發
2. 業務邏輯(Service層)具體業務邏輯
3.通用處理 (Manager層)service能力下沉,緩存,三方平台,與DAO交互。
4.數據持久(DAO層)數據訪問,與MySQL交互。
調用過程:
controller到processor之間是通過統一的接口分發。
所有的processor都有自定義注解,里面存接口url路徑名。啟動時通過ApplicationContext.getBeansOfType()把所有processor注冊到一個map里。注冊時getAnnocation判斷自定義注解不為空,把其中的接口名作為key,真實的processor作為value。
controller收到請求,轉發給統一接口,統一的接口通過收到的url接口名,從map里找到對應的processor轉發。processor直接調對應的flow。
問題現象:
在processor的方法上加@Transactional注解。此時調不到對應的processor。
調試發現,在啟動注冊時,該processor里就沒有注冊進來,根據url在map里自然找不到對應的processor。沒注冊進來發生在clazz.getAnnocation時候得到的為空。
從IOC容器中獲取所有processor的bean的時候,獲取到了所有processor。但是在含有@Transaction注解的那個processor在獲得自定義注解內容的時候獲取不到。
所以是@Transaction使通過getAnnotation()獲取自定義注解失效。
產生原因:
@Transaction 的原理是AOP代理,從而使IOC中的bean是代理類,不是真實bean。自定義注解並沒有繼承過來,所以在代理類上找不到自定義注解。
解決方法:
1. 自定義注解在聲明的時候加上 @Inherited,表明該注解是可以被子類繼承的。父類有該注解子類也可以獲取該注解。對類的繼承有效,對接口的繼承和實現無效。所以注意使用該方法的前提是AOP的動態代理使用的cglib而不是JDK自帶的動態代理。
cglib是支撐基於類的動態代理,而JDK自帶的是基於接口的動態代理。 使用cglib配置 <aop:aspectj-autoproxy proxy-target-class="true"/>
2. 將@Transaction下沉到flow層。最簡單也最合理。
3. 可先通過AOP的代理類獲取原始類,再通過原始類去獲取注解。
先判斷是不是Advised的實例。所有AOP代理的類都可以轉型為Advised接口,可通過它找到原始類。 instanceof Advised。通過 getTargetSource() getTarget() getClass() 獲取到原始類。https://blog.csdn.net/u011403655/article/details/52259566
由此可見雖然只在一個方法上加了@Transaction注解,但整個類都會被AOP代理。
為什么只有加@Transaction注解使自定義注解失效,而原先方法上的其他注解卻沒有影響到自定義注解?
@Transaction使用的是AOP的動態代理生成的代理類,其他注解不是AOP。
常見的AOP注解如:@Aspect,@Before,@After,@Around,@Transaction。
除了上面原因,還有什么情況,用getAnnotation獲取不到注解?
看注解的元注解@Retention定義的存活時間保留到什么時候,一般是Runtime,則可以在運行時通過getAnnotation獲取。其他如@Slf4j這個注解定義的存活時間是Source,為存活到編譯期。則獲取的就為null。