spring bean依赖注入/获取失败的若干种情况

文章来源:https://zhuanlan.zhihu.com/p/364658016

public interface IService {

	void doService();

}
@Service
public class ServiceImpl implements IService{

	@Override
	public void doService() {
		System.out.println("do service !!!");
	}
}

配置文件spring.xml

<context:component-scan base-package="mytest"/>
<!--开启AOP-->
<aop:aspectj-autoproxy />

启动类

public static void main(String[] args) {
	ClassPathXmlApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
        /** 同 
        @Autowired
	private IService iService;
        **/
	IService service=applicationContext.getBean(IService.class);
        //打印:do service !!!
	service.doService();
        /** 同 
        @Autowired
	private ServiceImpl iService;
        **/
	ServiceImpl serviceImpl=applicationContext.getBean(ServiceImpl.class);
        //打印:do service !!!
	serviceImpl.doService();
}

说明我们平时引用bean,可以通过其父类接口来引用,也可以通过具体实现类来引用,然而并没有这么简单,现在我新加了一个切面类

@Aspect
@Component
public class AspectTest {

	@Pointcut("execution(* *.doService(..))")
	public void test(){

	}
	@Before("test()")
	public void before(){
		System.out.println(" before -------");
	}

	@After("test()")
	public void after(){
		System.out.println(" after -------");
	}
}

再执行启动类,控制台会打印

在这里插入图片描述

而当我稍微修改下配置:

<aop:aspectj-autoproxy proxy-target-class="true"/>

则一切又正常了

在这里插入图片描述

这是为什么呢?首先不得不介绍下spring生成代理类的两种模式:jdk自带的Proxy代理与开源的CGLIB代理。当需要对bean生成代理类时,Spring会先判断是否配置了proxy-target-class=“true”,为true表示生成CGLIB代理类,否则会先判断bean是否有实现interface,有的话则生成Proxy代理类,不然就生成CGLIB代理类。

两种模式的区别就在于:

  • Proxy代理类是把代理对象(ServiceImpl)实现的interface(IService)自己实现了一遍,等于是给IService生成了个新的ServiceImpl2实现类,所以生成的代理类是IService的子类,和ServiceImpl类型无关
  • CGLIB代理类则是给代理对象(ServiceImpl)生成了个子类,所以代理类更是IService的子类

而applicationContext.getBean(xx.class)方法(@Autowired实际也会调用该方法)的实现中,会逐步调用如下方法:

  • DefaultListableBeanFactory#getBean
  • DefaultListableBeanFactory#resolveNamedBean
  • DefaultListableBeanFactory#getBeanNamesForType
  • DefaultListableBeanFactory#doGetBeanNamesForType(获取所有beans,遍历执行)
  • ResolvableType#isInstance
  • ResolvableType#isAssignableFrom(主要逻辑就是通过 xx.isAssignableFrom(other)方法判断)

isAssignableFrom就是判断某某是不是某某的父/同类, IService.class.isAssignableFrom(ServiceImpl.class)为true,反过来就是false

所以在未配置proxy-target-class="true"时,ServiceImpl实际上是Proxy代理类 P r o x y X X ,是 I S e r v i c e 的子类,所以通过 I S e r v i c e 能获取成功,而通过 S e r v i c e I m p l 获取时, S e r v i c e I m p l . c l a s s . i s A s s i g n a b l e F r o m ( ProxyXX,是IService的子类,所以通过IService能获取成功,而通过ServiceImpl获取时,ServiceImpl.class.isAssignableFrom( ProxyXX,是IService的子类,所以通过IService能获取成功,而通过ServiceImpl获取时,ServiceImpl.class.isAssignableFrom(ProxyXX.class)为false,所以getBean时会报NoSuchBeanDefinitionException异常,而注解注入时,当类型获取不到bean时会通过名称(‘serviceImpl’)来获取,再将得到的Proxy代理类反射设置属性时,与ServiceImpl类型不匹配,会报BeanNotOfRequiredTypeException异常;而配置proxy-target-class="true"后,IService/ServiceImpl.class.isAssignableFrom(Cglib代理.class)都为true,所以都能获取成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值