Spring Boot中如何记录带有@JsonIgnore注解的字段?
问题:带@JsonIgnore注解的字段无法被日志记录
使用Spring Boot 2.7.3、Java 17和Gradle,需要将所有数据库操作记录到日志文件中。目前其他字段日志输出正常,但UI记录类中带有@JsonIgnore注解的employeeCode和applicationNumber字段无法被记录。
切面类代码
@Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); @Pointcut("execution(public * org.mycomp.erp.service..*(..))") public void requestPointcut() {} @Before("requestPointcut()") public void requestLogger(JoinPoint joinPoint) throws JsonProcessingException { final ObjectMapper objectMapper = new ObjectMapper(); final String methodName = joinPoint.getSignature().getName(); final Object[] inputArguments = joinPoint.getArgs(); final String logMessage = "Operation : " + methodName + " With Input Parameters : " + objectMapper.writeValueAsString(inputArguments); logger.info(logMessage); } }
UI记录类代码
public record LeaveApplUx( @JsonIgnore String employeeCode, @JsonIgnore int applicationNumber, LocalDate startDate, LocalDate endDate, String leaveType) { public LeaveApplUx( @Size(min = 4, max = 5, message = "Employee Code Must Be Within 4 To 5 Character Long Or Blank") @JsonProperty("employeeCode") String employeeCode, @Range(min = 0, max = 9999, message = "Application Number Must Be Within 9999 Or Zero") @JsonProperty("applicationNumber") int applicationNumber, @NotNull(message = "Start Date Empty") @JsonSerialize(using = LocalDateSerializer.class) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") @JsonProperty("startDate") LocalDate startDate, @NotNull(message = "End Date Empty") @JsonSerialize(using = LocalDateSerializer.class) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") @JsonProperty("endDate") LocalDate endDate, @Size(min = 2, max = 2, message = "Leave Type Mis-Match") @JsonProperty("leaveType") String leaveType) { this.employeeCode = employeeCode; this.applicationNumber = applicationNumber; this.startDate = startDate; this.endDate = endDate; this.leaveType = leaveType; } }
请求示例
DELETE http://localhost:8080/hr/leave/application/delete
请求体:
{ "employeeCode": "E001", "applicationNumber": "1", "startDate": "01-04-2022", "endDate": "05-04-2022", "leaveType": "CL" }
当前日志输出
2022-08-30 14:32:53,688 INFO : Operation : deleteLeaveApplication With Input Parameters : ["N","N",{"startDate":"01-04-2022","endDate":"05-04-2022","leaveType":"CL"}]
解决方法
问题根源是Jackson的ObjectMapper序列化时会识别并跳过带@JsonIgnore注解的字段,而日志切面正是用它来序列化入参,导致这两个字段未被记录。以下是三种可行方案:
方案1:为日志序列化单独配置ObjectMapper,忽略@JsonIgnore注解
修改切面类中的ObjectMapper初始化逻辑,通过MixIn机制让Jackson在日志序列化时重新识别这两个字段:
@Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); // 定义MixIn,覆盖原类的@JsonIgnore配置 abstract static class LeaveApplUxLogMixIn { @JsonProperty String employeeCode; @JsonProperty int applicationNumber; } @Pointcut("execution(public * org.mycomp.erp.service..*(..))") public void requestPointcut() {} @Before("requestPointcut()") public void requestLogger(JoinPoint joinPoint) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper() .registerModule(new JavaTimeModule()) // 处理LocalDate类型 .addMixIn(LeaveApplUx.class, LeaveApplUxLogMixIn.class); final String methodName = joinPoint.getSignature().getName(); final Object[] inputArguments = joinPoint.getArgs(); final String logMessage = "Operation : " + methodName + " With Input Parameters : " + objectMapper.writeValueAsString(inputArguments); logger.info(logMessage); } }
方案2:使用Jackson视图(View)区分序列化场景
- 定义一个日志专用视图类:
public class LogView {}
- 修改LeaveApplUx的注解,保留@JsonIgnore同时添加日志视图注解:
public record LeaveApplUx( @JsonIgnore @JsonView(LogView.class) String employeeCode, @JsonIgnore @JsonView(LogView.class) int applicationNumber, LocalDate startDate, LocalDate endDate, String leaveType) { // 构造器代码不变 }
- 切面类中使用视图序列化:
ObjectMapper objectMapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); // 序列化时指定日志视图 String inputStr = objectMapper.writerWithView(LogView.class).writeValueAsString(inputArguments);
方案3:通过反射获取字段值(不依赖Jackson配置)
如果不想修改Jackson的序列化规则,可以通过反射遍历对象字段,手动拼接日志内容,示例如下:
private String getObjectLogStr(Object obj) throws IllegalAccessException { if (obj == null) return "null"; StringBuilder sb = new StringBuilder("{"); Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); sb.append(field.getName()).append(":").append(field.get(obj)).append(","); } if (sb.length() > 1) sb.deleteCharAt(sb.length()-1); sb.append("}"); return sb.toString(); }
在切面类中调用这个方法处理入参里的LeaveApplUx对象即可。
内容的提问来源于stack exchange,提问作者Deep
相关产品推荐
相关产品推荐

