2015-11-27 73 views
6

我遇到了一個很奇怪的異常,我不知道如何找到原因。春季開機devtools相關的推土機地圖異常

商業背景: 添加商品,同時它是價格表,一個商品有5個價格差異級別的用戶。

在控制器中,首先使用推土機將goodForm轉換爲商品,然後調用goodsService以節省商品。 在goodsService節能產品,商品遍歷價格表並填充goodsId商品價格,

GoodsForm: 
@Mapping("priceList") 
List<GoodsPriceForm> goodsPriceFormList; 
Goods: 
List<GoodsPrice> priceList; 

Controller: 
Goods goods = BeanMapper.map(goodsForm, Goods.class); 
goodsService.saveGoods(adminId, goods); 

GoodsService: 
goodsDao.save(goods); 
goods.getPriceList().forEach(p -> p.setGoodsId(goods.getId())); 
goodsPriceDao.save(goods.getPriceList()); 

但它拋出異常後:

2015-11-27 17:10:57,042 [http-nio-8081-exec-8] ERROR o.a.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: com.foo.goods.model.GoodsPrice cannot be cast to com.foo.goods.model.GoodsPrice] with root cause 
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] 

此錯誤消息讓我感到很困惑。另外我寫了一個單元測試,想重複這個,但失敗了。

GoodsForm form = new GoodsForm(); 
form.setGoodsPriceFormList(Lists.newArrayList(new GoodsPriceForm((byte) 1, BigDecimal.valueOf(10)), 
new GoodsPriceForm((byte) 2, BigDecimal.valueOf(9)), 
new GoodsPriceForm((byte) 3, BigDecimal.valueOf(8)))); 

Goods goods = BeanMapper.map(form, Goods.class); 
goods.getPriceList().forEach(p -> p.setGoodsId(goods.getId())); 

運行此單元測試,它執行正常。因此,爲什麼在真實的網絡情況下(Spring boot + Jpa)它失敗了,但是在單元測試的情況下它可以嗎?


Controller: 
System.out.println("PriceList: " + goods.getPriceList().getClass().getClassLoader());//PriceList: null 
System.out.println(goods.getPriceList().get(0).getClass().getClassLoader()); //java.lang.ClassCastException: com.foo.goods.model.GoodsPrice cannot be cast to com.foo.goods.model.GoodsPrice 

如果我生成的封裝罐,然後執行此罐

java -jar target/myapp.jar 

在這種情況下沒有上述異常。


我在pom.xml中評論了spring-boot-devtools,然後啓動應用程序,沒有出現上述例外。

+9

我有這樣的一個例外是,如果你加載相同的類唯一的一次與2個不同類的裝載機。你可以嘗試打印出每個對象的類加載器嗎? –

+5

然後相同的類被兩個不同的類加載器加載。首先的措施是在一個地方只有一個罐子。 –

+0

@Wim Deblauwe我已經試過了你的方式,請看這篇文章底部的補充內容 – zhuguowei

回答

8

默認情況下,IDE中的任何打開的項目都將使用「重新啓動」類加載器加載,而任何常規的.jar文件將使用「基本」類加載器加載。如果您使用多模塊項目,而不是將每個模塊導入到IDE中,則可能需要自定義。爲此,您可以創建一個META-INF/spring-devtools.properties文件。

spring-devtools.properties文件可以包含restart.exclude。和restart.include。前綴屬性。 include元素是應該拉入「重新啓動」類加載器的項目,而排除元素是應該下推到「基本」類加載器中的項目。該屬性的值是一個將應用於類路徑的正則表達式模式。

我的解決辦法:把META-INF/spring-devtools.properties內部資源文件夾,並添加以下內容

restart.include.dozer=/dozer-5.5.1.jar 

請參閱:http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-devtools-customizing-classload

+1

如果你使用推土機彈簧,請添加: restart.include.dozer-spring =/dozer-spring-5.5.1.jar – cLyric

0

您在這裏使用兩個不同的ClassLoader。裝載有兩個不同的ClassLoader的相同的Class被JVM視爲兩個不同的Class

修復此問題的解決方案很簡單:使用Interface

接口能夠抽象出這個問題,並且你可以在ClassLoaders之間交換它們實現的對象而沒有限制,只要你不直接引用實現。

+1

我在第一部分與你同行,但你的第二部分是完全錯誤的。從類加載器的角度看,接口也是類文件。所以'classLoader1.loadClass(「... MyInterface」)!= classLoader2.loadClass(「... MyInterface」)'。 –

+0

哦,你是對的,我不是那個意思,但它完全是這樣。我只是編輯了我的措詞,使其更清晰。 –

+1

我認爲這仍然是模糊的。如果您在OP上閱讀我的複製標記,您會看到,這是關於一個界面。只要您仍然使用兩個不同的類加載器加載接口,使用接口就沒有任何好處。我知道在有些情況下,_common父類loader_的接口或超類會有所幫助,但我認爲當前的問題不屬於這種情況之一。你仍然可以用一個例子證明我是錯誤的... –