Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serviceA' defined in class path resource [spring-bean.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String' to required type 'org.example.ioc.MyServiceB' for property 'serviceB'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.example.ioc.MyServiceB' for property 'serviceB': no matching editors or conversion strategy found
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
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:953)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at org.example.App.main(App.java:16)
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String' to required type 'org.example.ioc.MyServiceB' for property 'serviceB'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.example.ioc.MyServiceB' for property 'serviceB': no matching editors or conversion strategy found
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:595)
at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:609)
at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:219)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1756)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1712)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1452)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
... 11 more
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.example.ioc.MyServiceB' for property 'serviceB': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590)
... 17 more
由于在 spring-beans.xml 文件中使用
配置 serviceA 的依赖,导致 ioc 容器在实例化 serviceA 之后,调用 applyPropertyValues 方法进行属性值的注入时,PropertyValue 对应的值是 RuntimeBeanNameReference
而不是 RuntimeBeanReference
,所以经过 BeanDefinitionValueResolver 解析后得到的是 “serviceB” 字符串(serviceB 的 beanName)而不是 serviceB 对应的 bean 对象,因此在之后的 convertForProperty 方法中抛出类型转换异常。正确配置应该是使用 。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// ...
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// ...
// 遍历 BeanDefinition 中配置的所有属性值
// 即 spring-beans.xml 文件中配置的 property 子元素
// PropertyValue(nameA)
// PropertyValue(RuntimeBeanNameReference)
for (PropertyValue pv : original) {
// ...
String propertyName = pv.getName();// “serviceB”
Object originalValue = pv.getValue();// RuntimeBeanNameReference
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);// “serviceB”
Object convertedValue = resolvedValue;// “serviceB”
// 如果该属性有对应的 set 方法并且不是嵌套属性,接下来可以进行类型转换
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {// true
// 使用 PropertyEditor 或 ConversionService 进行类型转换
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// ...
}
// ...
}
spring bean 源码如下:
public class MyServiceA {
private String nameA;
private MyServiceB serviceB;
public void setNameA(String nameA) {
this.nameA = nameA;
}
public void setServiceB(MyServiceB serviceB) {
this.serviceB = serviceB;
}
}
public class MyServiceB {
private String nameB;
// 必须编写属性对应的 setter 方法,否则在属性注入时会抛出如下异常
// org.springframework.beans.NotWritablePropertyException:
// Invalid property 'nameB' of bean class [org.example.ioc.MyServiceB]:
// Bean property 'nameB' is not writable or has an invalid setter method.
// Does the parameter type of the setter match the return type of the getter?
public void setNameB(String nameB) {
this.nameB = nameB;
}
}
spring-beans.xml 配置文件如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="serviceA" class="org.example.ioc.MyServiceA">
<property name="nameA">
<value type="java.lang.String">
Smile
value>
property>
<property name="serviceB">
<idref bean="serviceB"/>
property>
bean>
<bean id="serviceB" class="org.example.ioc.MyServiceB">
<property name="nameB">
<value type="java.lang.String">
Silence
value>
property>
bean>
beans>