文章目录
- 1. 问题背景
- 2. `ResponseUtil.out` 方法分析
- a. 方法功能
- b. 序列化过程
- c. 注解 `@JsonInclude(JsonInclude.Include.NON_NULL)` 的作用
- 3. Java 对象如何被序列化为 JSON
- 4. 序列化的时机
- 5. 谁操作序列化
- 6. 自动序列化的条件
- 7. 总结
- 8. 可能的问题和注意
1. 问题背景
- 在
AdminAdminDTO
上添加@JsonInclude(JsonInclude.Include.NON_NULL)
注解后,Java 对象是否会自动序列化为 JSON 并返回给前端,以及这个序列化过程在什么时候发生、由谁操作的。 - 现在有了
ResponseUtil.java
,我们可以更具体地分析ResponseUtil.out
方法如何处理AdminAdminDTO
,以及序列化的细节。
2. ResponseUtil.out
方法分析
ResponseUtil.out
方法定义如下:
public static void out(HttpServletResponse response, BaseResult result) {
ObjectMapper mapper = new ObjectMapper();
PrintWriter writer = null;
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try {
writer = response.getWriter();
mapper.writeValue(writer, result);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
}
}
a. 方法功能
ResponseUtil.out
是一个工具方法,用于将BaseResult
对象(包含AdminAdminDTO
)序列化为 JSON 并写入HttpServletResponse
,最终以 JSON 格式返回给前端。- 它使用 Jackson 库(
com.fasterxml.jackson.databind.ObjectMapper
)进行序列化。
b. 序列化过程
-
输入:
BaseResult result
,其中result
是BaseResult.success(adminAdminDTO)
的调用结果,adminAdminDTO
是一个AdminAdminDTO
实例。 -
步骤:
-
创建
ObjectMapper
:ObjectMapper mapper = new ObjectMapper();
实例化一个 JacksonObjectMapper
,这是 Jackson 进行 JSON 序列化和反序列化的核心工具。ObjectMapper
默认会考虑类的注解(如@JsonInclude
)、字段的 getter/setter 方法以及 Jackson 的配置。
-
设置响应头:
response.setStatus(HttpStatus.OK.value());
设置 HTTP 状态码为 200(成功)。response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
设置响应内容的 MIME 类型为application/json;charset=UTF-8
,表明返回的是 JSON 格式数据。
-
获取
PrintWriter
并序列化:writer = response.getWriter();
获取HttpServletResponse
的输出流,用于写入 JSON 数据。mapper.writeValue(writer, result);
使用ObjectMapper
将BaseResult
对象(包含AdminAdminDTO
)序列化为 JSON 字符串,并写入PrintWriter
。- Jackson 会遍历
BaseResult
和AdminAdminDTO
的字段,基于 getter 方法和注解(如@JsonInclude(JsonInclude.Include.NON_NULL)
)生成 JSON。
-
刷新和关闭流:
writer.flush();
确保数据写入输出流。writer.close();
关闭PrintWriter
,释放资源。
-
-
输出:最终生成一个 JSON 字符串(如你提供的日志),通过 HTTP 响应返回给前端。例如:
{"code":0,"msg":"成功","data":{"communityLinkEditor":false,"copywritingEditor":false,"createdDate":1677778199000,...,"vip":true}}
c. 注解 @JsonInclude(JsonInclude.Include.NON_NULL)
的作用
AdminAdminDTO
上的@JsonInclude(JsonInclude.Include.NON_NULL)
会在序列化时生效,由 Jackson 的ObjectMapper
应用。- Jackson 会检查
AdminAdminDTO
的每个字段:- 如果字段值为
null
,不会序列化到 JSON(例如logo
、logoMin
等未设置的字段)。 - 如果字段值非
null
(包括false
、0
、字符串等),会序列化到 JSON(例如fakeComparor: true
、isVip: true
)。
- 如果字段值为
- 这解释了为什么你的日志中未返回
null
值的字段(如logo
、logoMin
),但返回了true
或其他非null
值的字段(如fakeComparor: true
)。
3. Java 对象如何被序列化为 JSON
-
过程:
AdminAdminDTO
是一个 Java 对象,包含字段和 getter/setter 方法。- Jackson 的
ObjectMapper
通过反射访问AdminAdminDTO
的 getter 方法(如isFakeComparor()
、isVip()
等),获取字段值。 - 根据字段的注解(如
@JsonInclude
)、字段值和ObjectMapper
的配置,Jackson 生成 JSON 字符串。 - JSON 字符串被写入
HttpServletResponse
,返回给前端。
-
字段映射:
- 字段如
isVip
被序列化为"vip": true
(见之前的分析,可能是 Jackson 的布尔属性命名规则或未显式注解导致)。 - 字段如
fakeComparor
被序列化为"fakeComparor": true
,因为它有明确的 getter/setter(isFakeComparor()
和setFakeComparor(boolean)
)。
- 字段如
4. 序列化的时机
- 序列化发生在
MyAuthenticationSuccessHandler.onAuthenticationSuccess
调用ResponseUtil.out
时:- 用户通过
/admin/signIn
或/admin/signInByPhone
登录,触发MyAuthenticationSuccessHandler.onAuthenticationSuccess
。 onAuthenticationSuccess
构建AdminAdminDTO
对象。- 调用
ResponseUtil.out(response, BaseResult.success(adminAdminDTO))
,触发序列化。
- 用户通过
- 具体时间点是 HTTP 响应生成时,
ObjectMapper.writeValue
将BaseResult
和AdminAdminDTO
转换为 JSON 字符串。
5. 谁操作序列化
- 直接操作者:
ResponseUtil.out
方法中的ObjectMapper.writeValue(writer, result)
由 Jackson 的ObjectMapper
执行序列化。 - 触发者:
- 后端开发者编写的代码(
MyAuthenticationSuccessHandler
和ResponseUtil
)调用了ObjectMapper
,触发序列化。 - Spring MVC 框架负责处理
HttpServletResponse
和 JSON 转换。如果ResponseUtil.out
没有显式指定 JSON 库,Spring MVC 可能会通过MappingJackson2HttpMessageConverter
(默认 Jackson 集成)自动完成序列化。
- 后端开发者编写的代码(
- JSON 库:在你的代码中,
ResponseUtil
显式使用了 Jackson(com.fasterxml.jackson.databind.ObjectMapper
),因此序列化由 Jackson 执行。但如果项目配置了 FastJSON(通过FastJsonHttpMessageConverter
),可能需要确认是否覆盖了 Jackson 的行为。
6. 自动序列化的条件
- 添加
@JsonInclude(JsonInclude.Include.NON_NULL)
后,序列化是“自动”的,前提是:AdminAdminDTO
被传递给支持 JSON 序列化的方法(如ResponseUtil.out
或 Spring MVC 的控制器返回)。- 项目配置了 Jackson 作为 JSON 序列化库,并启用了相关注解支持(
@JsonInclude
有效)。 - 字段有 getter 方法(
AdminAdminDTO
中的所有字段都有 getter 和 setter,因此满足条件)。
7. 总结
- 是否自动序列化:是的,
AdminAdminDTO
会自动序列化为 JSON 并返回给前端,前提是它被传递给ResponseUtil.out
或类似方法,Jackson 负责序列化。 - 序列化的时机:序列化发生在
MyAuthenticationSuccessHandler.onAuthenticationSuccess
中的ResponseUtil.out
调用时,具体由 Jackson 的ObjectMapper.writeValue
执行。 - 谁操作序列化:
- 由后端开发者编写的
ResponseUtil.out
触发序列化。 - 序列化操作由 Jackson 的
ObjectMapper
执行,框架(Spring MVC)通过MappingJackson2HttpMessageConverter
或ResponseUtil
协调。
- 由后端开发者编写的
- Java 对象如何成为 JSON:
AdminAdminDTO
对象通过其 getter 方法和注解(如@JsonInclude
)被 Jackson 序列化为 JSON 字符串,写入HttpServletResponse
,最终以application/json
格式返回给前端。
8. 可能的问题和注意
- 如果项目同时配置了 FastJSON(通过
FastJsonHttpMessageConverter
),需要确认ResponseUtil.out
是否优先使用 Jackson,还是被 FastJSON 覆盖。 - 如果
AdminAdminDTO
中的某些字段未返回(如logo
、logoMin
),可能是因@JsonInclude(JsonInclude.Include.NON_NULL)
过滤了null
值。 - 如果需要调试,建议在
ResponseUtil.out
中打印序列化前的adminAdminDTO
和序列化后的 JSON:logger.info("AdminAdminDTO before serialization: {}", adminAdminDTO); logger.info("AdminAdminDTO JSON: {}", mapper.writeValueAsString(result));