亂碼產生的三大入口
- 請求參數解碼:GET/POST 參數從字節 → 字符串
- 業務處理/存儲:字符串 → 數據庫/文件/外部 API
- 響應輸出編碼:字符串 → 字節流 → 瀏覽器
第一階段:請求參數統一解碼(核心難點)
1. POST 請求:Filter 方案(推薦)
@WebFilter("/*")
public class EncodingFilter implements Filter {
private static final String ENCODING = "UTF-8";
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
req.setCharacterEncoding(ENCODING); // 必須在 getParameter() 前調用
resp.setCharacterEncoding(ENCODING);
resp.setContentType("text/html;charset=" + ENCODING);
chain.doFilter(req, resp);
}
}
request.setCharacterEncoding() 僅對 Body 參數(POST)生效。
- 必須在
getParameter()、getReader() 之前調用,否則無效。
2. GET 請求:Tomcat URIEncoding(唯一生效)
<!-- Tomcat server.xml -->
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
... />
- GET 參數在 URL 路徑/查詢串,屬於 URI 解碼階段,Filter 無法攔截。
- Tomcat 8+ 默認即為 UTF-8,但建議顯式配置。
3. Spring Boot 統一配置
@Bean
public FilterRegistrationBean encodingFilter() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true); // 強制覆蓋 request/response 編碼
FilterRegistrationBean bean = new FilterRegistrationBean<>(filter);
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
第二階段:業務層字符串處理最佳實踐
| 場景 | 錯誤做法 | 正確做法 |
| 手動轉碼 | new String(str.getBytes("ISO-8859-1"), "GBK") | 全鏈路 UTF-8,禁止手動轉碼 |
| JDBC 連接 | 無參數 | jdbc:mysql://host:3306/db?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai |
| 數據庫表 | 默認 latin1 | CREATE TABLE t (...) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; |
| 文件讀寫 | new FileReader(file) | Files.readString(path, StandardCharsets.UTF_8) / new InputStreamReader(is, StandardCharsets.UTF_8) |
| JSON 序列化 | 默認可能轉義 | Jackson: mapper.disable(JsonGenerator.Feature.ESCAPE_NON_ASCII) |
第三階段:響應輸出編碼
// Servlet 原生
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter(); // 獲取 Writer 會鎖定編碼,之後不可改
out.write("中文正常");
// Spring MVC @RestController 默認 application/json;charset=UTF-8
// JSP 頁面頂部
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
全鏈路檢查清單(部署前必驗)
- ☐ 所有 .java/.jsp/.xml/.properties 文件保存為 UTF-8 無 BOM
- ☐ IDE 編譯編碼設置為 UTF-8(IntelliJ: Settings → File Encodings → Global/Project/Properties = UTF-8)
- ☐ Tomcat
server.xml URIEncoding="UTF-8"
- ☐
web.xml 或 Spring Boot 配置 CharacterEncodingFilter(forceEncoding=true)
- ☐ 數據庫連接串含
characterEncoding=utf8&useUnicode=true
- ☐ 表/庫字符集
utf8mb4(支持 Emoji)
- ☐ 前端頁面
<meta charset="UTF-8">、AJAX contentType: "application/json;charset=UTF-8"
- ☐ 日誌框架
log4j2.xml / logback.xml 設置 charset="UTF-8"
經典誤區還原
誤區 1:request.setCharacterEncoding("GBK") 就能解決所有亂碼
只對 POST Body 生效;GET 參數、響應輸出、數據庫存儲完全不受影響。
誤區 2:new String(bytes, "UTF-8") 就是萬能轉碼
若 bytes 本身已是錯誤解碼產生的(如 ISO-8859-1 讀取 UTF-8 字節),再轉 UTF-8 只會將錯誤固化。必須在最早入口正確解碼。
誤區 3:Tomcat 8 默認 UTF-8 就不用配了
默認值可能因版本、發行版、Docker 基礎鏡像而異。顯式配置零成本,隱式依賴高風險。
總結
中文亂碼本質是編碼/解碼不一致。解決之道只有一條:全鏈路統一 UTF-8(推薦 utf8mb4),在最早入口正確解碼,在最後出口正確編碼,中間業務層純 Unicode 操作,絕不手動轉碼。配置一次,永久安寧。
- 上一篇文章: Java Thread.join() 線程協作詳解:原理、用法、超時控制與常見誤區
- 下一篇文章: Java 字符串字符頻率統計:TreeMap 實現、Stream API 一行碼、性能對比與最佳實踐
Copyright ©
程式碼之家 All Rights Reserved