springboot2.x默認使用的代理是cglib代理操作
背景
因為項目優(yōu)化,打算寫個日志的切面類,于是起了個springboot 工程,在這里面測試。結(jié)果在springboot 里面測試正常,能正確打印日志,但是把代碼復制到實際項目中,在進入切面打印日志的時候總是報錯,報空指針錯誤。
經(jīng)調(diào)試發(fā)現(xiàn)每次都是在獲取注解上的屬性時報錯。當時百思不得解。后來靈光一閃,想到可能是項目中獲取到的是接口方法,而springboot是實現(xiàn)類的method ,所以可以拿到注解的屬性。
但是仔細一想,Springboot里面也是接口,難道不應(yīng)該走JDK動態(tài)代理嗎?那拿到這個方法的應(yīng)該也是接口的方法,帶著這個疑問,我開始了我的探索之旅。
驗證
springboot 項目

spring 項目

發(fā)現(xiàn)springBoot 竟然走的是cglib代理,起代理的是實現(xiàn)類,所以能拿到方法上注解的屬性,而我的項目是個傳統(tǒng)的spring 項目,service是接口,走的是JDK動態(tài)代理,通過切點拿到的是接口的方法,而接口上又沒有注解,所以按照springboot的寫法是拿不到注解的,拿不到注解也就拿不到注解屬性,所以報錯。
解決辦法
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項目的方法是先獲取目標類,然后再通過目標類獲取目標方法,然后再獲取方法上的注解。
深度追蹤
springboot 為什么將默認的代理改成了cglib,這會導致什么問題?如果我們想要事務(wù)走JDK動態(tài)代理,該如何做?
帶著這些疑問,我翻閱了springboot的相關(guān)issue ,發(fā)現(xiàn)很多人提這個問題。
先關(guān)issue如下:
springboot團隊之所以默認的代理模式設(shè)置成cglib代理,看看spring的官方團隊是怎么解釋的
This was changed in 1.4 (see 5423). We've generally found cglib proxies less likely to cause unexpected cast exceptions.
他們認為使用cglib更不容易出現(xiàn)轉(zhuǎn)換錯誤。springboot 默認的配置文件的位置在
/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ù)中強制使用JDK動態(tài)代理,以往的知識告訴我們,我們需要將proxyTargetClass 設(shè)置成false,于是我們在springboot 中發(fā)現(xiàn)注解@EnableTransactionManagement 或者@EnableAspectJAutoProxy默認就為false,說明這里面的屬性不起作用
@EnableAspectJAutoProxy(proxyTargetClass = false) @EnableTransactionManagement(proxyTargetClass = false)
同理 @EnableCaching 上的proxyTargetClass 屬性也是失效的。如果偏要springboot 走JDK動態(tài)代理,那么需要在application.properties里面配置
spring.aop.proxy-target-class=false
此時項目中走的就是JDK動態(tài)代理。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Kafka利用Java實現(xiàn)數(shù)據(jù)的生產(chǎn)和消費實例教程
這篇文章主要給大家介紹了關(guān)于Kafka利用Java實現(xiàn)數(shù)據(jù)的生產(chǎn)和消費的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-01-01
Java實現(xiàn)時間與字符串互相轉(zhuǎn)換詳解
這篇文章主要為大家詳細介紹了Java中實現(xiàn)時間與字符串互相轉(zhuǎn)換的相關(guān)方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2025-04-04
Java并發(fā)編程之詳解CyclicBarrier線程同步
在之前的文章中已經(jīng)為大家介紹了java并發(fā)編程的工具:BlockingQueue接口,ArrayBlockingQueue,DelayQueue,LinkedBlockingQueue,PriorityBlockingQueue,SynchronousQueue,BlockingDeque接口,ConcurrentHashMap,CountDownLatch,本文為系列文章第十篇,需要的朋友可以參考下2021-06-06

