Springboot 動態代理異常-because it is a JDK dynamic proxy that implements

用心編碼:

雷軍: 用互聯網的方式做企業,要靠 “專注、極致、口碑、快” 這 “七字訣”!



今天在重構一套原有系統時,項目啓動時,莫名其妙的報錯了,錯誤如下圖所示:
在這裏插入圖片描述

一、問題描述

***************************
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。


我的頭條:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章