Why choose CGLIB proxying as the default after SpringBoot 2.0

本文源码基于SpringBoot 2.6.4

一直以来Spring-aop对于实现了接口的Bean使用的是jdk的动态代理,对没有实现接口的Bean才使用Cglib代理。
但早在2019年3月看SpringBoot2.x的源码时发现,SpringBoot从2.0开始,SpringBoot将Spring-aop默认设置成了Cglib动态代理实现。

难道是出于性能考虑?:实际上在jdk1.8中,jdk的动态代理并不比cglib动态代理性能差,甚至有可能优于cglib性能。

当时特地给SpringBoot官网提了issue:Why choose CGLIB proxying as the default after SpringBoot 2.0 ?

SpringBoot官网并没有在github上直接解答这个疑问。

而SpringBoot的核心成员Phil Webb在stackoverflow上给出了答案!

Why choose CGLIB proxying as the default after SpringBoot 2.0
在这里插入图片描述
大概意思是说:基于jdk的动态代理特殊情况下可能导致ClassCastException,使用jdk的动态代理可能无法通过原始的实现类来注入。

For example:
有个接口和一个实现类

public interface ChatMsgService {
}

@Service
public class ChatMsgServiceImpl implements ChatMsgService{
    
    @Transactional
    public int save(ChatMsg chatMsg) {
       //...
    }
}

当通过接口注入时,无论使用的哪种代理方式,都能正常注入。

@Autowired
private ChatMsgService chatMsgService;

当通过具体实现类注入时,如果使用的cglib代码也能正常注入

@Autowired
private ChatMsgServiceImpl chatMsgService;

但如果使用的是jdk的动态代理时则会抛出如下异常:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'chatMsgServiceImpl' is expected to be of type 'com.bruce.integration.service.impl.ChatMsgServiceImpl' but was actually of type 'jdk.proxy2.$Proxy64'
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.checkBeanNotOfRequiredType(DefaultListableBeanFactory.java:1818)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1795)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1355)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
	... 74 more

为了避免这样的问题,SpringBoot2.0后默认将SpringAOP的动态默认设置成了cglib代码.

出现这个异常的原因其实也很简单:无论jdk还是cglib都是通过动态生成Class的方式来实现。但是jdk生成的class是实现了接口,和接口的原先实现类没有直接关系,而cglib生成的class是继承了整个实现类,可以强制转化给实现类。

那如果在SpringBoot中想让Spring针对接口使用jdk的动态代理可以使用如下配置:

spring.aop.proxy-target-class=false

参见SpringBoot2.6.4中AopAutoConfiguration的源码:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值