admin管理员组文章数量:1530065
前言
这篇博客是对【@Resource 源码解析】做的一个补丁。主要会讲解在使用@Resource的时候发生的is expected to be of type ‘xxx’ but was actually of type 'xxx’异常在源码层面是一个怎么样子的过程。为什么要用@Resource讲解,因为这个注解比较好模拟异常的发生,实际上@Autowired发生的原理也是一样的。更多Spring内容进入【Spring解读系列目录】。
问题还原
首先要有一个接口Demo
,然后有两个实现类DemoImpl1
和DemoImpl2
,以及一个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注入的。
版权声明:本文标题:源码解析异常is expected to be of type ‘xxx‘ but was actually of type ‘xxx‘ 是如何发生的 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1726601888a1077125.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论