在SpringBoot中棄用自帶的StandardServletMultipartResolver更換爲CommonsMultipartResolver也有坑

還是上篇傳統Spring項目換SpringBoot框架中的一個問題,先描述一下問題:

整和SpringBoot後系統中的文件上傳都不能用了,統統報錯如下:

大致就是Failed to parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadException: Stream closed這樣,通過異常棧信息可以看出當時使用的文件上傳解析器是CommonsMultipartResolver,在項目裏找了一下配置,由於之前是Spring項目確實是在spring-mvc.xml中配置了這個解析器:

問題就出在這裏。

 去Spring的官網上看了一眼,當然,是翻譯後看了一眼,MultipartResolver有兩種實現:

 而SpringBoot的autoconfigure那一套用哪個實現想必也是約定好的,看了一眼spring-boot-autoconfigure的spring.factories,果然找到了這行:

那就去看一下這個MultipartAutoConfiguration配置類:

看來SpringBoot默認用的是StandardServletMultipartResolver,而且這個@Bean後面還有一個@ConditionalOnMissingBean註解,意思是如果已經有MultipartResolver的實現Bean的就不註冊了,這做也是爲了同時註冊了兩個文件上傳解析器,IO流默認是隻能被讀取一次的(當然通過攔截器自己寫邏輯可以實現多次取出這裏不擴展可以參考https://blog.csdn.net/zhibo_lv/article/details/81875705),也就是說如果是上傳文件只能被解析一次,如果又來一個文件解析器不好意思流裏面已經沒東西了就有問題了。

理論上這裏有@ConditionalOnMissingBean註解StandardServletMultipartResolver應該不會被註冊了,但是我這個方法裏打了斷點,同時在CommonsMultipartResolver的構造方法中也打了斷點,項目啓動的時候居然都進來了,而且是先註冊了StandardServletMultipartResolver再註冊了CommonsMultipartResolver:

 先註冊了StandardServletMultipartResolver再註冊CommonsMultipartResolver那@ConditionalOnMissingBean當然就不起作用了,導致這個先後順序的原因就是因爲CommonsMultipartResolver是在XML中配置的Bean,而StandardServletMultipartResolver是在@Configuration配置類中配置的Bean,Spring在加載Bean的時候是先加載註解配置的Bean再從XML中加載Bean,這個在我之前的一篇博客中有詳細的寫(https://blog.csdn.net/weixin_42447959/article/details/104943589)。放這篇博客裏一張圖:

裏面四個load()方法,第一個是加載註解配置的,第二個是加載XML配置的,可以看出先後順序,詳細的可以參考那篇博客。 

所以如果SpringBoot要用CommonsMultipartResolver不能直接在XML中配置就完事了,當然也別鑽牛角尖同時用@Configuration配置類配置看看哪個先,這樣會導致同時註冊了兩個文件解析器,導致Stream Closed。正確的做法是先在yml配置文件中排除默認的MultipartResolver,如圖:

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration

然後CommonsMultipartResolver用註解配置也好用XML配置也好就隨便了。

當然我這裏的解決辦法就是直接在XML註釋了CommonsMultipartResolver的配置,SpringBoot自帶的StandardServletMultipartResolver難道不香嗎?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章