HttpServletResponse
无论你使用哪种框架,底层的核心都是 javax.servlet.http.HttpServletResponse 对象,它代表了服务器对客户端的响应,所有的输出操作,最终都是通过这个对象或其包装对象来完成的。
在原生 Java Servlet 中输出
这是最基础的方式,理解它有助于理解更高级框架的原理。
输出文本(HTML, JSON, XML 等)
使用 PrintWriter 或 OutputStream。
示例:输出 JSON 字符串
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/textOutput")
public class TextOutputServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 设置响应内容类型和字符编码
// 告诉浏览器我们返回的是 JSON,并使用 UTF-8 编码,防止中文乱码
response.setContentType("application/json;charset=UTF-8");
// 2. 获取 PrintWriter 对象
PrintWriter out = response.getWriter();
// 3. 写入响应内容
String jsonResponse = "{\"name\":\"张三\", \"age\":30, \"city\":\"北京\"}";
out.print(jsonResponse);
// 4. 关闭流(虽然 Tomcat 等容器会自动关闭,但显式关闭是好习惯)
out.close();
}
}
输出二进制文件(图片、PDF、Excel 等)
使用 OutputStream。
示例:下载一个文本文件
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/fileDownload")
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 设置响应头,告诉浏览器这是一个要下载的文件
response.setContentType("application/octet-stream");
// 设置下载文件的默认名称
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=\"example.txt\"";
response.setHeader(headerKey, headerValue);
// 2. 获取文件的输入流
File file = new File(getServletContext().getRealPath("/files") + File.separator + "example.txt");
FileInputStream inStream = new FileInputStream(file);
// 3. 获取响应的输出流
ServletOutputStream outStream = response.getOutputStream();
// 4. 使用缓冲区进行读写
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
// 5. 关闭流
inStream.close();
outStream.close();
}
}
在 Spring Boot 中输出
Spring Boot 极大地简化了 Response 的输出,主要通过 @ResponseBody 注解和 ResponseEntity。
使用 @ResponseBody(最简单)
直接在 Controller 方法上使用 @ResponseBody,返回的对象会被 Spring 的 HttpMessageConverter 自动序列化为 JSON(默认情况下)。
示例:返回一个对象自动转为 JSON
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // @RestController = @Controller + @ResponseBody
public class MyRestController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setName("李四");
user.setAge(25);
return user; // Spring Boot 会自动将这个 User 对象转为 JSON 并写入响应体
}
}
// 假设的 User 类
class User {
private String name;
private int age;
// getters and setters
}
访问 http://localhost:8080/user,你会得到类似 {"name":"李四","age":25} 的响应。
使用 ResponseEntity(最灵活)
ResponseEntity 允许你完全控制 HTTP 响应的状态码、响应头和响应体。
示例:自定义状态码和响应头
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
@RestController
public class ResponseEntityController {
@GetMapping("/customResponse")
public ResponseEntity<String> getCustomResponse() {
// 1. 创建自定义响应头
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "MyValue");
headers.add("Content-Type", "application/json;charset=UTF-8");
// 2. 创建响应体
String body = "{\"message\":\"这是一个自定义的响应\"}";
// 3. 返回 ResponseEntity,指定状态码、响应头和响应体
return new ResponseEntity<>(body, headers, HttpStatus.OK);
}
@GetMapping("/notFound")
public ResponseEntity<String> getNotFoundResponse() {
return new ResponseEntity<>("资源未找到", HttpStatus.NOT_FOUND);
}
}
直接写入 HttpServletResponse(不常用,但有时需要)
当你需要使用 OutputStream 写二进制文件时,可以直接注入 HttpServletResponse。
示例:下载文件
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class FileDownloadController {
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
// 设置响应头
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"report.pdf\"");
// 获取文件输入流
File file = new File("path/to/your/report.pdf");
InputStream inStream = new FileInputStream(file);
// 获取响应输出流并写入
org.apache.commons.io.IOUtils.copy(inStream, response.getOutputStream());
inStream.close();
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
注意:这种方式下,Controller 方法的返回类型通常是
void或String(如"forward:viewName"),因为你要手动操作response对象。
总结与对比
| 场景/框架 | 核心对象/注解 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 原生 Servlet | HttpServletResponse |
底层清晰,不依赖框架 | 代码繁琐,需要手动处理流、编码等 | 学习 Servlet 原理,旧项目维护 |
Spring Boot (@ResponseBody) |
@ResponseBody / @RestController |
极其简洁,自动序列化 | 对响应的控制力较弱(主要是状态码和体) | RESTful API 开发,返回 JSON/XML 等文本数据 |
Spring Boot (ResponseEntity) |
ResponseEntity |
最灵活,可全面控制响应 | 代码比 @ResponseBody 稍多 |
需要自定义状态码、响应头的复杂 API 响应 |
| Spring Boot (直接注入) | HttpServletResponse |
功能强大,可处理二进制流 | 违背了 Spring 的 MVC 设计理念,代码耦合 | 下载文件、流式响应等必须操作原始 response 的场景 |
最佳实践建议
- 对于 RESTful API:优先使用
@RestController+@ResponseBody,如果需要更精细的控制(如设置特定状态码、添加自定义头),升级到ResponseEntity,这是现代 Java Web 开发的主流方式。 - 对于文件下载:在 Spring Boot 中,推荐使用
ResponseEntity<byte[]>的方式,这样更符合 Spring 的设计模式,但如果文件特别大,直接注入HttpServletResponse并使用流式传输可能更节省内存。 - 始终注意字符编码:在输出文本(尤其是中文)时,务必设置正确的
Content-Type,包含charset=UTF-8,以防止乱码,在 Spring Boot 中,通常在application.properties中配置spring.http.encoding.charset=UTF-8即可全局解决。
