springboot專案與資料庫互動時中文編碼錯誤問題(非???亂碼)
-
背景
先概述一下我的專案編碼,專案使用的rpc框架讓前後端分離,前端傳遞瀏覽器的請求然後通過socket傳遞資料,遠端呼叫後端的服務。前端設定了tomcat使用utf-8編碼,後端資料來源使用utf8;mysql的client、mysqld、mysql system、資料表都是使用utf8mb4(mysql中的utf8)。你可以先確定自己專案中以上方面是否都使用了正確的編碼,再往下繼續看。 -
問題
當瀏覽器傳遞中文相關的查詢請求時,在windows執行的前端時顯示還是正常的(utf8形式輸出);前端通過socket傳遞給在windows執行的後端時顯示也是正常的(utf8輸出);但是當後端發查詢語句給在linux執行的mysql時,查閱日誌發現變成了生僻字,與原來不同(utf輸出,但還是中文),導致查詢沒有結果,如圖:
更奇怪的是,如果後端程式通過gui下的idea執行的話,表現正常;但是如果在powershell下用
mvn spring-boot:run
或是
java -jar xx.jar
執行後端程式的話就會像上面說的那樣表現異常。 -
問題排查
我試著把後端放在linux上,然後通過bash執行,發現表現正常。
於是瀏覽器->前端是正常的;前端->後端通過列印到控制檯是正常的(如果列印到powershell,要記得powershell預設是gbk,需先改內碼表為65001);然後到了mysql就變怪了,所以問題出在後端->資料庫。事實證明後端移到linux上也解決了問題。
想起來java的預設編碼好像與平臺相關,於是試著在powershell用
System.out.println(Charset.defaultCharset());
列印預設編碼,果然是gbk!然而在idea控制檯列印是utf-8!是windows的鍋。
- 問題原因
(下面將圖中那串生僻字用生僻字代替)
所以錯碼原因就是,前端將字串“莎士比亞”的utf-8編碼(二進位制形式記為x)通過socket發給後端,後端以為這是gbk編碼,所以utf8的"莎士比亞"代表的二進位制串x被翻譯成了gbk的 生僻字(二進位制表示也是x); 因為約定了與資料來源mysql通訊使用utf8,所以後端將gbk的生僻字
- 解決辦法
所以解決辦法就是去更改jvm執行時編碼。網上找了許多怎麼更改defaultCharset的方法,jvm啟動引數、環境變數什麼的方法,皆無效。遂在後端收取前端資料時的輸入輸出流指定編碼為utf8:
try (BufferedReader clientMessage = new BufferedReader(new InputStreamReader(
incomingSocket.getInputStream(), StandardCharsets.UTF_8)); PrintStream serverMessage = new PrintStream(
incomingSocket.getOutputStream(), true, StandardCharsets.UTF_8))
問題就得以解決了。