必须使用 response.getwriter() 获取容器管理的 printwriter,而非手动包装 bufferedwriter;输出文本时设 contenttype 和 characterencoding 后调用 getwriter(),输出二进制内容则用 getoutputstream()。

在 Web 容器(如 Tomcat)中,BufferedWriter 不能直接用于向 HttpServletResponse 写入字符流,因为响应的输出流/写入器必须由容器管理,且只能获取一次。错误地手动包装 response.getWriter() 或 response.getOutputStream() 可能导致 IllegalStateException、乱码或响应截断。
必须使用 response.getWriter() 获取 PrintWriter
HttpServletResponse 提供 getWriter() 方法返回一个容器管理的 PrintWriter,它内部已做了缓冲和字符编码适配(基于 response.setCharacterEncoding() 或默认配置)。这是唯一安全、标准的方式:
- 调用
response.setContentType("text/plain; charset=UTF-8")或类似类型(如"application/json") - 紧接着调用
response.setCharacterEncoding("UTF-8")(显式设置编码更可靠) - 再调用
PrintWriter out = response.getWriter()—— 此时容器已准备好响应头和编码 - 后续用
out.write()、out.println()等方法输出内容
不要自己 new BufferedWriter(out)
虽然 PrintWriter 构造函数支持传入 Writer 并启用自动刷新,但对 response.getWriter() 做二次包装是危险的:
-
BufferedWriter的缓冲可能延迟写出,而容器在service()方法返回后会尝试提交响应,导致缓冲未刷新、内容丢失 - 多次调用
getWriter()会抛出IllegalStateException -
BufferedWriter不感知 HTTP 响应生命周期,无法与容器同步刷新或关闭
需要缓冲时,让 PrintWriter 自己处理
PrintWriter 实例本身已具备缓冲能力(底层通常封装了 OutputStreamWriter + BufferedWriter),你只需合理控制刷新时机:
- 避免在循环中频繁调用
out.flush(),影响性能 - 若需确保实时输出(如流式响应、长连接),可在关键位置调用
out.flush(),容器会将缓冲区内容真正写到 socket - 不需要也不应该手动
out.close()—— 容器会在请求结束时自动关闭
二进制内容请用 getOutputStream()
如果输出的是非文本数据(如图片、PDF、ZIP),必须使用 response.getOutputStream(),此时不能调用 getWriter(),否则抛异常:
- 设置
response.setContentType("image/png") - 获取
ServletOutputStream os = response.getOutputStream() - 用
os.write(byte[])写入,必要时可包装为BufferedOutputStream(因ServletOutputStream本身不缓冲) - 同样,不要手动 close,也不要混用
getWriter()和getOutputStream()
本质上,Web 容器接管了响应输出的整个生命周期,开发者只需按规范获取并使用 PrintWriter 或 ServletOutputStream,其余交给容器处理。不复杂但容易忽略细节。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











