問題背景
使用springMVC框架,可以在Controller方法的注解中,指定返回?cái)?shù)據(jù)的格式(xml、html、key-value、json等),但是spring默認(rèn)的消息轉(zhuǎn)換器的默認(rèn)編碼標(biāo)準(zhǔn)是ISO-8859-1,該編碼是西歐編碼,無法對(duì)中文編碼,所以,前端收到數(shù)據(jù)之后,按照響應(yīng)頭中的編碼(ISO-8859-1)去解碼,關(guān)于中文的內(nèi)容都會(huì)得到亂碼,即使我們手動(dòng)采用UTF-8,也不能解碼。
關(guān)于亂碼的幾種解決方案
如何給springMVC框架指定一個(gè)編碼呢?網(wǎng)上的大部分的說法是開頭提到的:在controller的@RequestMapping頭中,定義produces屬性,比如:
@RequestMapping(value = "/path/map" produces = "application/json; charset=utf-8">
但是,按照我初學(xué)springMVC的資料,Demo里一開始就是這么寫的,初學(xué)者也不會(huì)去想為什么springMVC要采用json傳輸、能不能用別的個(gè)數(shù)傳輸數(shù)據(jù)
在前端請(qǐng)求數(shù)據(jù),后臺(tái)在控制臺(tái)打印的DEBUG信息里有一行:
Written [ json串 ] as "text/plain;charset=ISO-8859-1" using org.springframework.http.converter.StringHttpMessageConverter@34da3a54]
相繼搜索下述關(guān)鍵詞:
- Written xxx as "text/plain;charset=ISO-8859-1" using StringHttpMessageConverter
- springMVC json 亂碼
找到了配置編碼的方法:除了要在Controller中注釋以外,還需要在springMVC的相關(guān)配置文件(xml)中進(jìn)行配置。
我的配置文件分為三個(gè):spring-dao.xml、spring-service.xml和spring-web.xml文件,分別用于將springMVC與DAO層框架Mybatis整合、spring與service層整合、springMVC與web項(xiàng)目整合。所以,我的項(xiàng)目中,關(guān)于消息轉(zhuǎn)換器的配置,應(yīng)該在spring-web.xml文件中進(jìn)行配置。
按照網(wǎng)上的說法(排除掉關(guān)于GET、POST方法中含有中文,服務(wù)端收到亂碼的回答和博客):
說法一:
`<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>`
說法二:
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <array> <bean class = "org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> </bean> </array> </property> </bean>
參考來源:https://stackoverflow.com/questions/3616359/who-sets-response-content-type-in-spring-mvc-responsebody
說法三:
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
參考來源:http://blog.csdn.net/stationxp/article/details/38775295
說法四:
<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean>
參考來源:http://blog.csdn.net/jiaotuwoaini/article/details/51487605
最不靠譜的一種說法是,修改StringHttpMessageConverter的實(shí)現(xiàn),并且打包成jar。這種做法能夠暫時(shí)解決亂碼的問題,但是后期要是升級(jí)springMVC的版本,問題會(huì)再度出現(xiàn)。此外,對(duì)于Maven管理依賴的項(xiàng)目,移植到別的機(jī)器上,就會(huì)變成原生springMVC的jar包(如果修改的包不同名,那么就會(huì)找不到依賴)。所以這個(gè)不是解決方案。
上述方案的分析與嘗試
上述說法大致有兩個(gè)方案,一個(gè)是關(guān)于mvc:annotation-driven,一個(gè)是直接配置一些bean。但是,按照第一種方式配置之后,初始化的時(shí)候就會(huì)拋異常。第二種方式也不起作用,應(yīng)該是缺少什么。
第一種方式的異常一般都是:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'workItemsSubmitController' method public java.lang.Object imp4sep.controller.WorkItemsSubmitController.submitEGPAdd(int,java.util.List<imp4sep.po.EngineeringGeophysicsInfo>,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) to {[/work/submit/GDSP/add],methods=[POST],produces=[application/json;charset=utf-8]}: There is already 'workItemsSubmitController' bean method public java.lang.Object imp4sep.controller.WorkItemsSubmitController.submitGDSPAdd(int,java.util.List<imp4sep.po.GeologicalDrillingSamplingInfo1>,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) mapped.
尋找異常的原因
為了確定導(dǎo)致問題的原因,我相繼把mvc:annotation-driven標(biāo)簽的子標(biāo)簽和屬性移除,到最后,只要配置了<mvc:annotation-driven/>,就會(huì)拋這個(gè)異常。
搜索下述關(guān)鍵詞:
- 開啟mvc:annotation-driven 導(dǎo)致BeanCreationException RequestMappingHandlerMap
- Ambiguous mapping. Cannot map 'xxx' method xxx to xx: There is already 'xx'
一些博客記錄了相同的問題,一般是初學(xué)者的demo項(xiàng)目,發(fā)現(xiàn)是Controller的映射路徑重復(fù),移除其中一個(gè)controller。我沒有在意。
參考來源:http://www.cnblogs.com/davidwang456/p/4387654.html
還有說法:
bean重復(fù)初始化:使用@controller注解初始化一次,在applicationContext中又定義一次
參考來源:http://www.fanfanyu.cn/news/staticpagefile/2351.html
XML校驗(yàn)報(bào)錯(cuò)
在這中間還出現(xiàn)了一個(gè)小插曲:spring-*.xml文件相繼校驗(yàn)出錯(cuò)。
cvc-complex-type.2.4.c: 通配符的匹配很全面, 但無法找到元素 'mvc:annotation-driven'
在搜索報(bào)錯(cuò)信息的過程中找到了意外一個(gè)博客
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> </list> </property> </bean>
``
<mvc:annotation-driven />
參考來源:http://www.cnblogs.com/feiyujun/p/6581683.html
這篇博客的最后還提到springMVC版本的的問題,以至于后面我在找到問題真正所在前,開始懷疑高版本的配置文件與低版本不同。
此外,介紹的配置方式能夠解決亂碼問題,但是其中強(qiáng)調(diào)的配置順序卻并不能解決IE提示下載的問題。
在一個(gè)springMVC的QQ群里問了亂碼的問題,有三個(gè)群友給出兩個(gè)不同的配置:
- 配置一:
可能他的是低版本的配置,我將MappingJacksonHttpMessageConverter改成MappingJackson2HttpMessageConverter了:
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8" /> <property name="writeAcceptCharset" value="false" /> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
- 配置二
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/plain;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean>
<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="text" /> <constructor-arg index="1" value="plain" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="*" /> <constructor-arg index="1" value="*" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="text" /> <constructor-arg index="1" value="*" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="application" /> <constructor-arg index="1" value="json" /> <constructor-arg index="2" value="UTF-8" /> </bean> </list> </property> </bean>
發(fā)現(xiàn)錯(cuò)誤的根本原因
嘗試了兩天仍然沒有解決的亂碼問題,今天我仔細(xì)看了一下異常信息,由于方法名相似,之前一直以為異常的信息所說的模糊映射是由于“多處配置”所致,
原來真的存在重復(fù)的映射。這個(gè)問題根結(jié)的發(fā)現(xiàn),同時(shí)也說明一個(gè)問題:開啟注解驅(qū)動(dòng)之后,首次啟動(dòng)初始化的時(shí)候,會(huì)遍歷全部的映射路徑,確保不存在重復(fù)映射,否則會(huì)拋出BeanCreationException異常。
解決問題
找到存在的重復(fù)映射路徑,修改過來之后,重新發(fā)布
-
無亂碼的,但不能美化的輸出
按照群友1給出的配置之后,前端發(fā)來請(qǐng)求,得到?jīng)]有亂碼的數(shù)據(jù),如下:
firefox瀏覽器展示的樣式
原始數(shù)據(jù)
點(diǎn)擊美化輸出無效?!邦^”標(biāo)簽里的響應(yīng)頭信息:
請(qǐng)求響應(yīng)頭信息 -
無亂碼且可以美化的輸出
按照群友2所說的配置:
firefox瀏覽器展示的樣式
原始數(shù)據(jù)
請(qǐng)求響應(yīng)頭信息 無亂碼,可美化,接受編碼類型多的方案
`<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
校驗(yàn)會(huì)報(bào)錯(cuò):
這種校驗(yàn)報(bào)錯(cuò)時(shí)有時(shí)無,據(jù)說校驗(yàn)時(shí),除了從網(wǎng)絡(luò)位置校驗(yàn),還會(huì)從本地的jar包中校驗(yàn),不知道eclipse是不是這么做的。
原始數(shù)據(jù)標(biāo)簽下,點(diǎn)擊“美化輸出”,能夠進(jìn)行排版:
頭標(biāo)簽的響應(yīng)數(shù)據(jù)也不同:
- 無亂碼、不能美化的輸出
網(wǎng)上找到的原配置是錯(cuò)誤的,如下:
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean lass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>
發(fā)布之后會(huì)拋異常:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Multiple 'property' definitions for property 'supportedMediaTypes' Offending resource: file [D:\Apache\apache-tomcat-9.0.0.M18\webapps\imp4sep\WEB-INF\classes\spring\spring-web.xml] Bean '' -> Property 'supportedMediaTypes'
修改為下述形式才可以:
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean lass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> </bean> <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>
HTTP頭是這樣的:
上述可行方案的對(duì)比
在IE下都會(huì)會(huì)產(chǎn)生下載提示