springboot2.x默认使⽤的代理是cglib代理操作
背景
因为项⽬优化,打算写个⽇志的切⾯类,于是起了个springboot ⼯程,在这⾥⾯测试。结果在springboot ⾥⾯测试正常,能正确打印⽇志,但是把代码复制到实际项⽬中,在进⼊切⾯打印⽇志的时候总是报错,报空指针错误。
经调试发现每次都是在获取注解上的属性时报错。当时百思不得解。后来灵光⼀闪,想到可能是项⽬中获取到的是接⼝⽅法,⽽springboot是实现类的method ,所以可以拿到注解的属性。
但是仔细⼀想,Springboot⾥⾯也是接⼝,难道不应该⾛JDK动态代理吗?那拿到这个⽅法的应该也是接⼝的⽅法,带着这个疑问,我开始了我的探索之旅。
验证
springboot 项⽬
spring 项⽬
发现springBoot 竟然⾛的是cglib代理,起代理的是实现类,所以能拿到⽅法上注解的属性,⽽我的项⽬是个传统的spring 项⽬,service是接⼝,⾛的是JDK动态代理,通过切点拿到的是接⼝的⽅法,⽽
接⼝上⼜没有注解,所以按照springboot的写法是拿不到注解的,拿不到注解也就拿不到注解属性,所以报错。
解决办法
springboot的写法
private Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
//获取⽅法签名
Method method = ((MethodSignature) Signature()).getMethod();
return method;
}
private String getAnnotationDesc(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Method method = getMethod(joinPoint);
String value = Annotation(MyLog.class).value();
return value;
}
spring 的写法
private Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
//获取⽅法签名
Class<?> targetClass = Target().getClass();
String methodName = Signature().getName();
Class[] parameterTypes = ((MethodSignature) Signature()).getParameterTypes();
Method method = Method(methodName, parameterTypes);
return method;
}
private String getAnnotationDesc(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Method method = getMethod(joinPoint);
String value = Annotation(MyLog.class).value();
return value;
}
可以看到spring项⽬的⽅法是先获取⽬标类,然后再通过⽬标类获取⽬标⽅法,然后再获取⽅法上的注解。
深度追踪
springboot 为什么将默认的代理改成了cglib,这会导致什么问题?如果我们想要事务⾛JDK动态代理,该如何做?
带着这些疑问,我翻阅了springboot的相关issue ,发现很多⼈提这个问题。
先关issue如下:
springboot团队之所以默认的代理模式设置成cglib代理,看看spring的官⽅团队是怎么解释的
This was changed in 1.4 (see 5423). We've generally found cglib proxies less likely to cause unexpected cast exceptions.
他们认为使⽤cglib更不容易出现转换错误。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
},
如果在事务中强制使⽤JDK动态代理,以往的知识告诉我们,我们需要将proxyTargetClass 设置成false,于是我们在springboot 中发现注解
@EnableTransactionManagement 或者@EnableAspectJAutoProxy默认就为false,说明这⾥⾯的属性不起作⽤
@EnableAspectJAutoProxy(proxyTargetClass = false)
@EnableTransactionManagement(proxyTargetClass = false)
同理 @EnableCaching 上的proxyTargetClass 属性也是失效的。如果偏要springboot ⾛JDK动态代理,那么需要在application.properties⾥⾯配置
spring.aop.proxy-target-class=falsespring boot是啥
此时项⽬中⾛的就是JDK动态代理。
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论