采用 Java 原生序列化 + HTTP POST 实现对象级请求/响应。注意:生产环境强烈建议改用 JSON/Protobuf 以规避反序列化漏洞(CVE-2015-4852 等),本文仅作技术原理演示。
public class RequestPayload implements Serializable {
private static final long serialVersionUID = 1L;
private String action;
private Map params = new HashMap<>();
// 全参构造器、getter/setter、toString()
}
public class ResponsePayload implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String message;
private Object data;
// 全参构造器、getter/setter
}
public class ObjectHttpClient {
private final CloseableHttpClient httpClient;
private final String baseUrl;
public ObjectHttpClient(String baseUrl) {
this.baseUrl = baseUrl;
this.httpClient = HttpClients.custom()
.setConnectionManager(new PoolingHttpClientConnectionManager())
.evictIdleConnections(30, TimeUnit.SECONDS)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(15000)
.build())
.build();
}
public ResponsePayload send(RequestPayload request) throws IOException, ClassNotFoundException {
HttpPost post = new HttpPost(baseUrl + "/api/object");
post.setHeader("Content-Type", "application/x-java-serialized-object");
post.setHeader("Accept", "application/x-java-serialized-object");
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(request);
oos.flush();
post.setEntity(new ByteArrayEntity(bos.toByteArray()));
try (CloseableHttpResponse response = httpClient.execute(post)) {
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new IOException("HTTP " + status);
}
try (ObjectInputStream ois = new ObjectInputStream(response.getEntity().getContent())) {
return (ResponsePayload) ois.readObject();
}
}
}
}
public void close() throws IOException {
httpClient.close();
}
}
@WebServlet("/api/object")
public class ObjectProcessServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(ObjectProcessServlet.class);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 校验 Content-Type
String ct = req.getContentType();
if (ct == null || !ct.contains("application/x-java-serialized-object")) {
resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Require application/x-java-serialized-object");
return;
}
// 2. 反序列化请求
RequestPayload request;
try (ObjectInputStream ois = new ObjectInputStream(req.getInputStream())) {
request = (RequestPayload) ois.readObject();
} catch (ClassNotFoundException | IOException e) {
log.warn("Deserialize request failed", e);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid serialized object");
return;
}
// 3. 业务处理(示例)
ResponsePayload response;
try {
Object result = processBusiness(request);
response = new ResponsePayload(200, "SUCCESS", result);
} catch (BusinessException e) {
response = new ResponsePayload(e.getCode(), e.getMessage(), null);
} catch (Exception e) {
log.error("Business process error", e);
response = new ResponsePayload(500, "INTERNAL_ERROR", null);
}
// 4. 序列化响应
resp.setContentType("application/x-java-serialized-object");
try (ObjectOutputStream oos = new ObjectOutputStream(resp.getOutputStream())) {
oos.writeObject(response);
oos.flush();
}
}
private Object processBusiness(RequestPayload req) {
// 实际业务逻辑:路由到 Service 层
return "Processed: " + req.getAction() + " with " + req.getParams().size() + " params";
}
}
PoolingHttpClientConnectionManager:复用 TCP 连接,避免频繁握手开销setConnectTimeout/setSocketTimeout:防止网络抖动导致线程无限阻塞evictIdleConnections:定期清理空闲连接,防止服务端主动断开导致客户端复用失败// 正确模式:try-with-resources 保证关闭顺序
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
oos.flush(); // 必须 flush,确保数据写入底层字节数组
byte[] data = bos.toByteArray();
} // 自动关闭 oos -> bos,释放内存
严禁手动 close() 后继续使用流对象,或在 finally 中重复关闭导致异常掩盖。
显式声明 serialVersionUID,避免跨 JVM 版本/编译器差异导致 InvalidClassException。字段增删改需遵循兼容性规则:
-Djdk.serialFilter=... 白名单过滤(JDK 9+)ValidatingObjectInputStream (Apache Commons IO) 限制可反序列化类Content-Length 上限(如 1MB),防止大对象 OOM 攻击public class TestMain {
public static void main(String[] args) throws Exception {
try (ObjectHttpClient client = new ObjectHttpClient("https://localhost:8080")) {
RequestPayload req = new RequestPayload("QUERY", Map.of("id", 1001, "type", "user"));
ResponsePayload resp = client.send(req);
System.out.println("Code: " + resp.getCode());
System.out.println("Msg: " + resp.getMessage());
System.out.println("Data: " + resp.getData());
}
}
}
启动 Servlet 容器(Tomcat/Jetty),运行测试类,控制台输出业务处理结果,验证对象双向传输成功。