Spring Boot如何动态处理Setter/Getter字段名(动态月份场景)
解决方案
针对动态月份字段的序列化问题,推荐两种可行实现方案:
方案1:使用HashMap直接处理(快速实现)
这种方式无需定义实体类,直接用Map存储固定字段和动态字段,Spring会自动完成JSON序列化。
ServiceImpl层代码
@Service public class UserRecordServiceImpl implements UserRecordService { @Autowired private UserRecordMapper userRecordMapper; @Override public List<Map<String, Object>> getRecentSixMonthsRecords() { // 计算近6个月的时间范围 LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusMonths(6); // 从数据库查询记录(MyBatis直接返回List<Map>即可) List<Map<String, Object>> dbRecords = userRecordMapper.selectRecentSixMonthsRecords(startDate, endDate); List<Map<String, Object>> resultList = new ArrayList<>(); for (Map<String, Object> record : dbRecords) { Map<String, Object> resultMap = new HashMap<>(); // 存入固定字段 resultMap.put("userName", record.get("user_name")); resultMap.put("userEmail", record.get("user_email")); // 处理动态月份字段:将数据库返回的月份编码转换为英文名称 for (Map.Entry<String, Object> entry : record.entrySet()) { String dbKey = entry.getKey(); if (dbKey.startsWith("month_")) { // 解析月份编码(例如month_202401 -> 202401) String monthCode = dbKey.substring(6); YearMonth yearMonth = YearMonth.parse(monthCode, DateTimeFormatter.ofPattern("yyyyMM")); // 转换为英文月份名称(如January) String monthName = yearMonth.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); resultMap.put(monthName, entry.getValue()); } } resultList.add(resultMap); } return resultList; } }
Controller层代码
@RestController @RequestMapping("/user/records") public class UserRecordController { @Autowired private UserRecordService userRecordService; @GetMapping("/recent-six-months") public ResponseEntity<List<Map<String, Object>>> getRecentSixMonthsRecords() { List<Map<String, Object>> records = userRecordService.getRecentSixMonthsRecords(); return ResponseEntity.ok(records); } }
方案2:自定义实体类结合Jackson注解(规范可扩展)
通过@JsonAnyGetter和@JsonAnySetter注解,让实体类支持动态字段的序列化与反序列化,同时保留固定字段的类型安全。
自定义实体类
import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import java.util.HashMap; import java.util.Map; public class UserMonthRecord { // 固定字段 private String userName; private String userEmail; // 存储动态月份字段的容器 private Map<String, Object> dynamicMonthFields = new HashMap<>(); // 固定字段的Getter/Setter public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserEmail() { return userEmail; } public void setUserEmail(String userEmail) { this.userEmail = userEmail; } // 动态字段的序列化注解:将Map中的键值对作为顶级字段输出 @JsonAnyGetter public Map<String, Object> getDynamicMonthFields() { return dynamicMonthFields; } // 动态字段的反序列化注解:接收任意未知字段并存入Map @JsonAnySetter public void setDynamicMonthField(String key, Object value) { this.dynamicMonthFields.put(key, value); } }
ServiceImpl层代码
@Service public class UserRecordServiceImpl implements UserRecordService { @Autowired private UserRecordMapper userRecordMapper; @Override public List<UserMonthRecord> getRecentSixMonthsRecords() { LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusMonths(6); List<Map<String, Object>> dbRecords = userRecordMapper.selectRecentSixMonthsRecords(startDate, endDate); List<UserMonthRecord> resultList = new ArrayList<>(); for (Map<String, Object> record : dbRecords) { UserMonthRecord userRecord = new UserMonthRecord(); // 设置固定字段 userRecord.setUserName((String) record.get("user_name")); userRecord.setUserEmail((String) record.get("user_email")); // 存入动态月份字段 for (Map.Entry<String, Object> entry : record.entrySet()) { String dbKey = entry.getKey(); if (dbKey.startsWith("month_")) { String monthCode = dbKey.substring(6); YearMonth yearMonth = YearMonth.parse(monthCode, DateTimeFormatter.ofPattern("yyyyMM")); String monthName = yearMonth.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); userRecord.setDynamicMonthField(monthName, entry.getValue()); } } resultList.add(userRecord); } return resultList; } }
Controller层代码
@RestController @RequestMapping("/user/records") public class UserRecordController { @Autowired private UserRecordService userRecordService; @GetMapping("/recent-six-months") public ResponseEntity<List<UserMonthRecord>> getRecentSixMonthsRecords() { List<UserMonthRecord> records = userRecordService.getRecentSixMonthsRecords(); return ResponseEntity.ok(records); } }
方案对比
- 方案1:无需额外定义类,实现成本低,适合快速开发;缺点是缺乏类型安全,后续维护难度稍高。
- 方案2:保留固定字段的类型检查,扩展性更强,适合需要长期维护或后续可能增加固定字段的场景。
内容的提问来源于stack exchange,提问作者Hanufa Hannan
相关产品推荐
相关产品推荐

