用心編碼:
雷軍: 用互聯網的方式做企業,要靠 “專注、極致、口碑、快” 這 “七字訣”!
今天在重構一套原有系統時,項目啓動時,莫名其妙的報錯了,錯誤如下圖所示:
一、問題描述
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'attachCacheService' could not be injected as a 'com.abc.service.AttachCacheService' because it is a JDK dynamic proxy that implements:
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
Process finished with ex 類it code 1
錯誤的意思是:AttachCacheService 類採用的是 JDK動態代理的方式實現的,需要我們考慮使用基於CGLib方式實現動態代理。
二、代碼分析
1.服務層(service)沒有按照傳統方式定義, 沒有定義接口及實現。而是直接定義實現類,如下:
@Service
public class AttachCacheService{
@Transactional(rollbackFor = Exception.class)
public String upload(MultipartFile file) {
// upload file...
return "http://test.com/abcde.jpg";
}
}
2.在網上查詢這個錯誤時,我得到以下幾種解決方案:
(1)在啓動類上加上 @EnableTransactionManagement(proxyTargetClass = true)
(2)在配置文件配置 spring.aop.proxy-target-class=true
(3)按照傳統方式定義 接口及實現類
三、深入理解Springboot 動態代理及CGLib
1.JDK動態代理
JDK動態代理:利用Java反射機制生成一個實現代理接口的匿名類,通過InvokeHandler來處理。
針對接口類生成代理。
2.CGLib動態代理
CGLib動態代理:利用開源asm包對代理對象類(class)文件動態加載,並且修改字節碼數據實現動態代理。
針對類生成實現代理。
(1)若目標對象實現了接口(IService + ServiceImpl) ,那麼Springboot 會採用JDK動態代理實現AOP
(2)如果目標對象沒有實現接口(Service),那麼必須採用CGLib 實現AOP
四、總結
1.重點是上面方法上加上了@Transactional(rollbackFor = Exception.class)註解,該註解的目標主要是用於控制數據庫事務,那麼它的內部必須通過AOP切面的方法去實現事務控制,所以會用到動態代理,而且是採用CGLib。
2.一般情況下 實現接口類,接口類採用JDK代理,實現類採用CGLib代理
3.Springboot2.x默認使用CGLib代理,官方如下解釋:
This was changed in 1.4 (see 5423). We’ve generally found cglib proxies less likely to cause unexpected cast exceptions.
需要將 spring.aop.proxy-target-class屬性值設置爲true 調用cglib
4.當設置過 @EnableAsync和@EnableCaching 時,spring.aop.proxy-target-class 默認爲false。
我的頭條: