springboot2.x默認(rèn)使用的代理是cglib代理操作
背景
因?yàn)轫?xiàng)目?jī)?yōu)化,打算寫個(gè)日志的切面類,于是起了個(gè)springboot 工程,在這里面測(cè)試。結(jié)果在springboot 里面測(cè)試正常,能正確打印日志,但是把代碼復(fù)制到實(shí)際項(xiàng)目中,在進(jìn)入切面打印日志的時(shí)候總是報(bào)錯(cuò),報(bào)空指針錯(cuò)誤。
經(jīng)調(diào)試發(fā)現(xiàn)每次都是在獲取注解上的屬性時(shí)報(bào)錯(cuò)。當(dāng)時(shí)百思不得解。后來靈光一閃,想到可能是項(xiàng)目中獲取到的是接口方法,而springboot是實(shí)現(xiàn)類的method ,所以可以拿到注解的屬性。
但是仔細(xì)一想,Springboot里面也是接口,難道不應(yīng)該走JDK動(dòng)態(tài)代理嗎?那拿到這個(gè)方法的應(yīng)該也是接口的方法,帶著這個(gè)疑問,我開始了我的探索之旅。
驗(yàn)證
springboot 項(xiàng)目
spring 項(xiàng)目
發(fā)現(xiàn)springBoot 竟然走的是cglib代理,起代理的是實(shí)現(xiàn)類,所以能拿到方法上注解的屬性,而我的項(xiàng)目是個(gè)傳統(tǒng)的spring 項(xiàng)目,service是接口,走的是JDK動(dòng)態(tài)代理,通過切點(diǎn)拿到的是接口的方法,而接口上又沒有注解,所以按照springboot的寫法是拿不到注解的,拿不到注解也就拿不到注解屬性,所以報(bào)錯(cuò)。
解決辦法
springboot的寫法
private Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { //獲取方法簽名 Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); return method; } private String getAnnotationDesc(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { Method method = getMethod(joinPoint); String value = method.getAnnotation(MyLog.class).value(); return value; }
spring 的寫法
private Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { //獲取方法簽名 Class<?> targetClass = joinPoint.getTarget().getClass(); String methodName = joinPoint.getSignature().getName(); Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Method method = targetClass.getMethod(methodName, parameterTypes); return method; } private String getAnnotationDesc(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { Method method = getMethod(joinPoint); String value = method.getAnnotation(MyLog.class).value(); return value; }
可以看到spring項(xiàng)目的方法是先獲取目標(biāo)類,然后再通過目標(biāo)類獲取目標(biāo)方法,然后再獲取方法上的注解。
深度追蹤
springboot 為什么將默認(rèn)的代理改成了cglib,這會(huì)導(dǎo)致什么問題?如果我們想要事務(wù)走JDK動(dòng)態(tài)代理,該如何做?
帶著這些疑問,我翻閱了springboot的相關(guān)issue ,發(fā)現(xiàn)很多人提這個(gè)問題。
先關(guān)issue如下:
springboot團(tuán)隊(duì)之所以默認(rèn)的代理模式設(shè)置成cglib代理,看看spring的官方團(tuán)隊(duì)是怎么解釋的
This was changed in 1.4 (see 5423). We've generally found cglib proxies less likely to cause unexpected cast exceptions.
他們認(rèn)為使用cglib更不容易出現(xiàn)轉(zhuǎn)換錯(cuò)誤。springboot 默認(rèn)的配置文件的位置在
/org/springframework/boot/spring-boot-autoconfigure/2.1.7.RELEASE/spring-boot-autoconfigure-2.1.7.RELEASE.jar!/META-INF/spring-configuration-metadata.json
{ "name": "spring.aop.proxy-target-class", "type": "java.lang.Boolean", "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue": true },
如果在事務(wù)中強(qiáng)制使用JDK動(dòng)態(tài)代理,以往的知識(shí)告訴我們,我們需要將proxyTargetClass 設(shè)置成false,于是我們?cè)趕pringboot 中發(fā)現(xiàn)注解@EnableTransactionManagement 或者@EnableAspectJAutoProxy默認(rèn)就為false,說明這里面的屬性不起作用
@EnableAspectJAutoProxy(proxyTargetClass = false) @EnableTransactionManagement(proxyTargetClass = false)
同理 @EnableCaching 上的proxyTargetClass 屬性也是失效的。如果偏要springboot 走JDK動(dòng)態(tài)代理,那么需要在application.properties里面配置
spring.aop.proxy-target-class=false
此時(shí)項(xiàng)目中走的就是JDK動(dòng)態(tài)代理。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄
這篇文章主要介紹了Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06Java Http請(qǐng)求傳json數(shù)據(jù)亂碼問題的解決
這篇文章主要介紹了Java Http請(qǐng)求傳json數(shù)據(jù)亂碼問題的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09JAVA實(shí)現(xiàn)的簡(jiǎn)單萬年歷代碼
這篇文章主要介紹了JAVA實(shí)現(xiàn)的簡(jiǎn)單萬年歷代碼,涉及Java日期操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10java中springMVC獲取請(qǐng)求參數(shù)的方法
這篇文章主要介紹了java中springMVC獲取請(qǐng)求參數(shù)的方法,springmvc是spring框架的一個(gè)模塊,springmvc和spring無需通過中間整合層進(jìn)行整合,需要的朋友可以參考下2023-05-05解決IDEA中下載free maven plugin插件無效的問題
這篇文章主要介紹了解決IDEA中下載free maven plugin插件無效的問題,本文通過圖文并茂的形式給大家分享解決方案,供大家參考,需要的朋友可以參考下2020-11-11Java ThreadLocal詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲(chǔ),本文會(huì)詳細(xì)的介紹一下,有興趣的可以了解一下2017-06-06Spring?Boot項(xiàng)目如何優(yōu)雅實(shí)現(xiàn)Excel導(dǎo)入與導(dǎo)出功能
在我們平時(shí)工作中經(jīng)常會(huì)遇到要操作Excel的功能,比如導(dǎo)出個(gè)用戶信息或者訂單信息的Excel報(bào)表,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot項(xiàng)目中如何優(yōu)雅實(shí)現(xiàn)Excel導(dǎo)入與導(dǎo)出功能的相關(guān)資料,需要的朋友可以參考下2022-06-06MyBatisPlus的IService接口實(shí)現(xiàn)
MyBatisPlus是一個(gè)為MyBatis提供增強(qiáng)的工具,它通過IService接口簡(jiǎn)化了數(shù)據(jù)庫的CRUD操作,IService接口封裝了一系列常用的數(shù)據(jù)操作方法,本文就來介紹一下,感興趣的可以了解一下2024-10-10Spring Cloud入門系列服務(wù)提供者總結(jié)
這篇文章主要介紹了Spring Cloud入門系列之服務(wù)提供者總結(jié),服務(wù)提供者使用Eureka Client組件創(chuàng)建 ,創(chuàng)建完成以后修改某文件,具體操作方法及實(shí)例代碼跟隨小編一起看看吧2021-06-06MybatisPlus字段自動(dòng)填充失效,填充值為null的解決方案
這篇文章主要介紹了MybatisPlus字段自動(dòng)填充失效,填充值為null的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01