异常堆栈:
Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.util.LinkedHashMap] to required type [java.util.concurrent.ConcurrentHashMap] for property 'handlerNameHashMap'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [java.util.LinkedHashMap] to required type [java.util.concurrent.ConcurrentHashMap] for property 'handlerNameHashMap': no matching editors or conversion strategy found`
这个异常时我在项目的配置文件注册一个工厂类之后,部署时抛出来的。
我的工厂类配置文件:
<!-- handler工厂 -->
<bean id="handlerFactory" class="com.taobao.wmpevent.server.handler.HandlerFactory">
<property name="handlerNameHashMap" >
<map>
<entry key="ORDER_STATUS" value-ref="saleOrderEventHandler"/>
<entry key="PACKAGE_STATUS" value-ref="packageEventHandler"/>
</map>
</property>
</bean>
我的工厂类:
public class HandlerFactory {
private static ConcurrentHashMap<String, BaseHandler> handlerNameHashMap=new ConcurrentHashMap<String, BaseHandler>();
....
起先看到这个还以为我的Spring版本过低(2.5.6),然后就去对比了下也没发现差异,然后就去跟了下代码。
异常是在这段代码抛出的:
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
// Definitely doesn't match: throw IllegalArgumentException.
StringBuffer msg = new StringBuffer();
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
if (propertyName != null) {
msg.append(" for property '" + propertyName + "'");
}
if (editor != null) {
msg.append(": PropertyEditor [" + editor.getClass().getName() + "] returned inappropriate value");
}
else {
msg.append(": no matching editors or conversion strategy found");
}
throw new IllegalArgumentException(msg.toString());
}
上面的if语句里的requiredType是我要转换的ConcurrentHashMap,convertedValue这个是在方法的开始赋值的:
protected Object convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class requiredType,
PropertyDescriptor descriptor, MethodParameter methodParam)
throws IllegalArgumentException {
Object convertedValue = newValue;
然后沿着这个方法的调用链追溯,一直到AbstractAutowireCapableBeanFactory类的applyPropertyValues方法时才看到:
String propertyName = pv.getName();
Object originalValue = pv.getValue();
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
最后一行代码的resolvedValue值就是上面我们提到的newValue,这个值是在resolveValueIfNecessary这个方法创建的:
else if (value instanceof ManagedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, (Map) value);
}
/**
* For each element in the ManagedMap, resolve reference if necessary.
*/
private Map resolveManagedMap(Object argName, Map mm) {
Map resolved = new LinkedHashMap(mm.size());
Iterator it = mm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey());
Object resolvedValue = resolveValueIfNecessary(
argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + entry.getKey() + BeanWrapper.PROPERTY_KEY_SUFFIX,
entry.getValue());
resolved.put(resolvedKey, resolvedValue);
}
return resolved;
}
看到这里才明白堆栈里的Linked HashMap就是这里创建的,它当然不能转换成我需要的ConcurrentHashMap了。
于是我就纳闷了,难道就不能注入ConcurrentHashMap了,应该会有解决方案的吧,搜索了下,果然天无绝人之路啊!
Well ofcourse you get an error….
ConcurrentHashMap isn’t a LinkedHashMap and vice versa…
Also you should be programming to a Map instead of a concrete
implementation that way it wouldn’t matter what you inject, A HashMap
a LinkedHashMap or a ConcurrentHashMap all would work.The map element generates a LinkedHashMap by default if you want to
create another one specify it with the targetMapClass property.
这是在一个帖子里看到的答案,顺着targetMapClass 这个关键词就摸到瓜了。
Spring中 ‘MapFactoryBean‘ 类给开发者提供了在Spring配置文件中创建具体的Map集合类的方法.
这个类的源码在这里可以看到:
http://dynaspring.googlecode.com/svn/trunk/src/dynaspring/support/MapFactoryBean.java
重新修改我的配置文件:
<!-- handler工厂 -->
<bean id="handlerFactory" class="com.taobao.wmpevent.server.handler.HandlerFactory">
<property name="handlerNameHashMap" >
<bean class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="targetMapClass">
<value>java.util.concurrent.ConcurrentHashMap</value>
</property>
<property name="sourceMap">
<map>
<entry key="ORDER_STATUS" value-ref="saleOrderEventHandler"/>
<entry key="PACKAGE_STATUS" value-ref="packageEventHandler"/>
</map>
</property>
</bean>
</property>
</bean>
部署时就不抛异常了。