源码解析异常is expected to be of type ‘xxx‘ but was actually of type ‘xxx‘ 是如何发生的

前言

这篇博客是对【@Resource 源码解析】做的一个补丁。主要会讲解在使用@Resource的时候发生的is expected to be of type ‘xxx’ but was actually of type 'xxx’异常在源码层面是一个怎么样子的过程。为什么要用@Resource讲解,因为这个注解比较好模拟异常的发生,实际上@Autowired发生的原理也是一样的。更多Spring内容进入【Spring解读系列目录】

问题还原

首先要有一个接口Demo,然后有两个实现类DemoImpl1DemoImpl2,以及一个DemoService去依赖Demo。当我们在上面的例子中使用@Resource时,如果变量名字不符合Spring命名规则,而项目里也没有指定一个命名规则怎么办。这个问题其实很容易模拟,比如修改一下DemoService的内容如下。

public class DemoService {
    @Resource
    DemoImpl2 demoImpl1;
}

报错实例

可以看到修改后的例子中的变量名已经不符合Spring默认的命名规则了。如果碰见这种情况Spring会怎么处理呢。这里剧透一下,会直接报错。而且这个错误相信只要是Spring初学者大概率会遇到,就是类型不匹配的错误。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoService': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'demoImpl1' is expected to be of type 'com.example.bean.DemoImpl2' but was actually of type 'com.example.bean.DemoImpl1'
   at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:321)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1415)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
   at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
   at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
   at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
   at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
   at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
   at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925)
   at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
   at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
   at com.example.test.Test.main(Test.java:9)
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'demoImpl1' is expected to be of type 'com.example.bean.DemoImpl2' but was actually of type 'com.example.bean.DemoImpl1'
   at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:412)
   at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:468)
   at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527)
   at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
   at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:650)
   at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:229)
   at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
   at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:318)
   ... 12 more

Spring调用链

那么这个错误是在哪里引发的呢?继续去源码里面探索。为了简便这个过程,直接拿出调用栈出来。

doGetBean:256, AbstractBeanFactory (org.springframework.beans.factory.support) [2]
getBean:213, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveBeanByName:468, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
autowireResource:527, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
getResource:497, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
getResourceToInject:650, CommonAnnotationBeanPostProcessor$ResourceElement (org.springframework.context.annotation)
inject:229, InjectionMetadata$InjectedElement (org.springframework.beans.factory.annotation)
inject:119, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:318, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
populateBean:1415, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:608, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:531, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 232307208 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$34)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support) [1]
getBean:208, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:944, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:925, AbstractApplicationContext (org.springframework.context.support)
refresh:588, AbstractApplicationContext (org.springframework.context.support)
<init>:93, AnnotationConfigApplicationContext (org.springframework.context.annotation)
main:9, Test (com.example.test)

源码追踪

根据调用栈的过程,最后还是到了doGetBean()这个方法里面。

protected <T> T doGetBean(
         String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
         throws BeansException {
      String beanName = transformedBeanName(name);
      Object bean;
      // Eagerly check singleton cache for manually registered singletons.
      Object sharedInstance = getSingleton(beanName);
      if (sharedInstance != null && args == null) {
         if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
               logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                     "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
               logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
         }
         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
      }else {
      		/**无关,略**/
      }
	  //判断生成的类型是不是和需要的类型一致
      if (requiredType != null && !requiredType.isInstance(bean)) {
         try {//如果不一致,是否可以强制转换
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
               throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
         }//如果无法转换,捕获转换异常,报错
         catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
               logger.trace("Failed to convert bean '" + name + "' to required type '" +
                     ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
         }
      }
      return (T) bean;
   }

这次进来要更进一步分析,首先看doGetBean()方法的参数:name就是字段名字,在这个例子中是'demoImpl1'requiredType就是需要被注入的类型DemoImpl2,也就是;在后面两个不重要略过。根据我们上面的分析,bean对象将会根据name拿出来,因此走过第一个if以后bean的值就是demoImpl1对象。那么现在程序直接到了类型检查if (requiredType != null && !requiredType.isInstance(bean))这个语句块。首先requiredType != null是成立的;然后requiredType.isInstance(bean))这个一定是false,但是加了非,所以也变成了ture。这里为什么要这样判断呢?因为考虑到如果两个类型可以进行互相转换那么其实也是可以成立并赋值的。下面convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType)就是尝试进行转化。如果转换成功直接返回bean就可以了。如果无法转换报错,捕获异常抛出去。因此当发现类型不匹配的时候就会引发报错。

相关异常解析链接:
【源码解析异常expected single matching bean but found 2是如何发生的】
这篇文章里有分析,@Resource是如何进行byType注入的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值