項目接入了配置中心,之前選用的都是Spring Cloud相關(guān)組件底瓣,所以直接使用的就是Spring Cloud Config在接入的時候裕菠,一旦配置文件有數(shù)組相關(guān)配置,不管是properties文件還是yml文件都會報錯竖螃。
錯誤信息如下
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON document: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '[' (code 91) excepted space, or '>' or "/>"
at [row,col {unknown-source}]: [1,1899] (through reference chain: org.springframework.cloud.config.environment.Environment["propertySources"]->java.util.ArrayList[3]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '[' (code 91) excepted space, or '>' or "/>"
at [row,col {unknown-source}]: [1,1899] (through reference chain: org.springframework.cloud.config.environment.Environment["propertySources"]->java.util.ArrayList[3])
跟蹤客戶端源碼發(fā)現(xiàn)初始化獲取配置信息是由ConfigServicePropertySourceLocator對象的locate()方法觸發(fā)
客戶端調(diào)用此方法會觸發(fā)到服務(wù)端的EnvironmentController中的labelled()方法淑廊,該方法會從相應(yīng)的配置倉庫返回配置信息,封裝成Environment對象返回
看起來好像都沒什么問題斑鼻,可是實際執(zhí)行序列化和反序列化都是使用的Spring MVC的HttpMessageConverter來處理的蒋纬,如圖
這里可以明顯看到客戶端并沒有要求服務(wù)端必須返回json猎荠,那么服務(wù)端處理的時候選擇按照messageConverter的順序來判斷是否支持頭信息進(jìn)行序列化的
可以看到服務(wù)端選擇MappingJackson2XmlHttpMessageConverter這個轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換坚弱,所帶頭信息為application/xml蜀备,這就悲劇了,實際上有數(shù)組的情況下這個轉(zhuǎn)換器支持不了的荒叶。我們是希望用MappingJackson2HttpMessageConverter這個轉(zhuǎn)換器來轉(zhuǎn)換碾阁,它所支持的頭信息為application/json,這才是正確的些楣。脂凶。
由于客戶端寬泛的限制導(dǎo)致服務(wù)端并沒有處理好這個問題,我這里直接使用切面強(qiáng)制返回json數(shù)據(jù)并限制了頭信息為application/json保證客戶端解析的正確性愁茁。下面列出代碼
/**
* @author xiezhengchao
* @since 17/12/27 上午10:20.
* 解決1.4.0.RELEASE版本返回值為數(shù)組時所引發(fā)的bug,如果后續(xù)版本修復(fù)了可以不需要這個切面配置
*/
@Aspect
@Component
public class EnvironmentAspect{
private final Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
@Pointcut("execution(* org.springframework.cloud.config.server.environment.EnvironmentController.labelled(..))")
public void getResource(){}
@Around("getResource()")
public Object labelledJson(ProceedingJoinPoint pjp) throws Throwable{
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
Object object = pjp.proceed();//執(zhí)行該方法
finalOutput(response, object);
return null;
}
private void finalOutput(HttpServletResponse response, Object result) throws IOException{
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
String jsonStr = gson.toJson(result);
writeData(response, jsonStr);
}
private void writeData(HttpServletResponse response, String jsonStr) throws IOException{
try {
response.getOutputStream().write(jsonStr.getBytes("UTF-8"));
} finally {
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
}
通過這種方法確保了客戶端返回的值都是通過json處理的蚕钦,解決了上面的問題。
其他
這個問題是完全沒想到會找了兩天才解決鹅很,去官網(wǎng)翻了一圈都沒發(fā)現(xiàn)配置數(shù)組問題的,google也沒搜到促煮。邮屁。只能看源碼翻了。
實際上如果你只是引用了spring cloud config那么不會出現(xiàn)這個問題菠齿,這是其他包的引用導(dǎo)致的佑吝,但是我還是覺得客戶端在發(fā)送的時候應(yīng)該直接限制好json這樣就沒有這么多問題了。