Controller中為什么不能寫@Transactional
原文鏈接:http://sunbingbing.cn/controller中為什么不能寫transactional/
1.背景
Controller指SpringMVC項目中用於定義接口信息的類,該類一般會被@Controller或@RestController等SpringMVC相關注解標記;
@Transactional指spring-tx包中定義的事務注解,被該注解標記的方法或類將成為一個整體,“同進同退”;
在開發過程中,注解是我們的神兵利器,但如果不恰當的使用將會造成嚴重的問題。
2.問題
- 在Controller層的接口定義處添加@Transactional
- 對Controller層進行統一代理,添加公共校驗等攔截
- 添加過@Transactional的Controller接口失效,請求返回404
3.解決方案
方案一:調整@Transactional到service層
方案二:添加cglib依賴,指定強制使用cglib代理
4.問題分析
-
從@Transactional入手分析;Spring在掃描bean 的時候,發現某些類或方法上添加了事務注解,就會生成該類的代理類,並賦予代理類事務的相關邏輯,從而達到事務效果;我們在實際調用中,調用的是對應的代理類而非其本身。
-
從Controller層代理入手分析;通過相關工具類例如BeanNameAutoProxyCreater對controller進行統一代理,並插入統一的校驗邏輯,達到快速開發的目的;同樣的在實際調用中,我們調用到的也是其對應的代理類而非其本身。
*從代理方面入手分析;SpringAOP部分主要使用JDK動態代理或cglib代理;默認情況下,如果被代理的目標對象實現了至少一個接口,則會使用jdk動態代理,否則會通過cglib創建代理類,但也可以通過設置強制使用cglib進行代理操作。
*結合以上分析,404的Controller被代理了兩次,因controller沒有實現接口,所以第一次代理一定是cglib代理;因設置強制使用cglib代理可解決404問題反推,第二次代理一定是jdk動態代理;結合以上推測,第二次jdk動態代理時可能導致@RequestMapping等注解失效,最終造成404問題。
5.相關問題
本次問題的引發原因是對現有工程新增統一驗證邏輯導致,同步發現的問題還有Controller中的自動注入失敗,導致執行service中邏輯時報404;原因時Controller中方法為private,private的方法不會被代理,導致引用的service屬性沒有完成注入。
6.開發規范
- Controller中不要添加@Transactional等注解,該類注解一律放到service中;
- Controller中被定義為接口的方法不要定義為private
7.加buffer
public static void main(String[] args){
//生成代理類的class文件到工程根目錄
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//生成cglib代理的class文件到指定目錄
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"e:\\cglib");
SpringApplication.run(Application.class);
}