分析spring devtools与dozer的兼容问题
某个项目开启spring devtools后,某些地方遍历列表抛出异常ClassCastException。
debug + google 知除了类型不一致,ClassLoader不一致也会抛ClassCastException。
发现当源对象class A { List<B> b }用dozer(net.sf.dozer:dozer:5.5.1)转换为目标对象class C { List<B> b }时,将转化后的对象的成员变量b执行遍历, 会抛出异常ClassCastException。
dozer转换后的对象使用的ClassLoader为RestartClassLoader,但成员变量b的是Launcher$AppClassLoader。
如果直接定义变量b,其ClassLoader为RestartClassLoader。明显dozer在转换对象创建成员变量b是使用了不同的ClassLoader。
阅读dozer的源码,知dozer通过反射封装成员变量的信息,然后匹配将成员变量的值赋值到目标对象。
记录字段的类名
// since we are mapping some sort of collection now is a good time to decide
// if they provided hints
// if no hint is provided then we will use generics to determine the mapping type
if (fieldMap.getDestHintContainer() == null) {
Class<?> genericType = fieldMap.getGenericType(BuilderUtil.unwrapDestClassFromBuilder(destObj));
if (genericType != null) {
HintContainer destHintContainer = new HintContainer();
destHintContainer.setHintName(genericType.getName());
FieldMap cloneFieldMap = (FieldMap) fieldMap.clone();
cloneFieldMap.setDestHintContainer(destHintContainer); // should affect only this time as fieldMap is cloned
fieldMap = cloneFieldMap;
}
}
然后通过hintName加载类
public static Class<?> loadClass(String name) {
BeanContainer container = BeanContainer.getInstance();
DozerClassLoader loader = container.getClassLoader();
return loader.loadClass(name);
}
而DozerClassLoader通过BeanContainer的类加载器构造。
public class BeanContainer {
DozerClassLoader classLoader =
new DefaultClassLoader(getClass().getClassLoader());
}
然而spring devtools并没有包含 dozer的jar,即BeanContainer使用的是默认的类加载器Launcher$AppClassLoader。
显然只需要告诉devtools包含dozer的jar即可。
解决办法:
- 在资源目录下创建
META-INF/spring-devtools.properties - 将下面内容加到
META-INF/spring-devtools.propertiesrestart.include.dozer=/dozer-[\\w\\d-\.]+\.jar
附: