业务背景:
添加商品以及价格,一个商品会有多个价格(针对不同用户等级).
代码:
先在controller中使用dozer将goodsForm-->Goods,然后保存商品, 之后遍历商品价格列表,注入商品ID,接着保存商品价格.
GoodsForm: List<GoodsPriceForm> priceList; Goods: List<GoodsPrice> priceList; Controller: Goods goods = BeanMapper.map(goodsForm, Goods.class); save(goods); goods.getPriceList().forEach(p->p.setGoodsId(goods.getId())); save(goods.getPriceList());
在遍历商品价格列表时报错, 错误信息:
java.lang.ClassCastException: com.foo.goods.model.GoodsPrice cannot be cast to com.foo.goods.model.GoodsPrice at com.foo.goods.service.GoodsService$$Lambda$11/310447431.accept(Unknown Source) ~[na:na] at java.util.ArrayList.forEach(ArrayList.java:1249) ~[na:1.8.0_51] at com.foo.goods.service.GoodsService.saveGoods(GoodsService.java:34) ~[classes/:na]
对这一异常描述有些摸不着头脑, 经提醒有可能是不同的类加载器加载导致, 在Eclipse debug模式中Expression视图下,输出了goods和goodsPrice的类加载器名
Goods: org.springframework.boot.devtools.restart.classloader.RestartClassLoader@41aa15ea GoodsPrice: sun.misc.Launcher$AppClassLoader@58644d46
果然涉及到不同的类加载器.
尝试了在pom.xml中注释掉spring-boot-devtools, 再启动后没有上述异常. 但如何在保留devtools的情况下解决该问题呢? 查询官方文档:
By default, any open project in your IDE will be loaded using the “restart” classloader, and any regular
.jar
file will be loaded using the “base” classloader. If you work on a multi-module project, and not each module is imported into your IDE, you may need to customize things. To do this you can create aMETA-INF/spring-devtools.properties
file.The
spring-devtools.properties
file can containrestart.exclude.
andrestart.include.
prefixed properties. Theinclude
elements are items that should be pulled-up into the “restart” classloader, and theexclude
elements are items that should be pushed down into the “base” classloader. The value of the property is a regex pattern that will be applied to the classpath.
于是在工程的resources目录下添加了META-INF/spring-devtools.properties,内容为
restart.include.dozer=/dozer-5.5.1.jar
即dozer代码也使用RestartClassLoader,可以解决上述问题.
补充:
在该工程中的Application类中有如下一段代码
@Bean public Mapper fooDozerBeanMapper() { DozerBeanMapper mapper = BeanMapper.getDozerBeanMapper(); List<String> mappingFiles = Lists.newArrayList(mapper.getMappingFiles()); mappingFiles.add("foo-dozer-custom-convert.xml"); mapper.setMappingFiles(mappingFiles); return mapper; }
其中BeanMapper是依赖另一个工程(bar), 当在Eclipse中关闭该依赖工程(bar)后, 会报错
Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of org/springframework/boot/devtools/restart/classloader/RestartClassLoader) previously initiated loading for a different type with name "org/dozer/DozerBeanMapper" at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_05] at java.lang.ClassLoader.defineClass(ClassLoader.java:760) ~[na:1.8.0_05] at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_05]
解决方法
修改spring-devtools.properties,再增加一个配置
restart.include.bar=/bar.jar restart.include.dozer=/dozer-5.5.1.jar