分析spring devtools与dozer的兼容问题

少于 1 分钟阅读

某个项目开启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转换后的对象使用的ClassLoaderRestartClassLoader,但成员变量b的是Launcher$AppClassLoader

如果直接定义变量b,其ClassLoaderRestartClassLoader。明显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即可。

解决办法:

  1. 在资源目录下创建META-INF/spring-devtools.properties
  2. 将下面内容加到META-INF/spring-devtools.properties
    restart.include.dozer=/dozer-[\\w\\d-\.]+\.jar
    

附: