不积跬步,无以至千里。不积小流,无以成江海。
通常我们有这种需求,某一个字段是由前台维护的:编码 <==> 含义,类似一个键值对。含义是可能会变化的,但编码是固定的,一般我们都会在数据库中直接存储编码,但数据返回给前台显示时需要展示为含义。当然我们可以在从数据库查询时去join到该值的含义一起返回,这里提供另一种方式来实现这个功能。
实现思路 主要是使用注解加切面。在数据返回前通过切面把被注解的字段替换成该值的含义。
这里使用性别字段,键值关系为:
M - 男
W - 女
新建注解 新建一个注解用于标识字段需要被切面处理
1 2 3 4 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SexValue {}
新建第二个注解,作为一个切点。
1 2 3 4 5 @Target(value = ElementType.METHOD ) @Retention(value = RetentionPolicy.RUNTIME) public @interface ProcessResult {}
注解参数含义:
Entity 实体类如下,在 sex
字段上添加注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Person { private Long id; private String name; private Integer age; @SexValue private String sex; private String address; private String phoneNum; private LocalDateTime createdDate; private LocalDateTime lastUpdateDate; private Long objectVersionNumber; public void createBefore () { this .createdDate = LocalDateTime.now(); this .lastUpdateDate = LocalDateTime.now(); this .objectVersionNumber = 1L ; } public void updateBefore (Long objectVersionNumber) { this .lastUpdateDate = LocalDateTime.now(); this .objectVersionNumber = objectVersionNumber + 1L ; } }
Controller 在Controller
中,把需要翻译的方法加上注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @RestController @RequestMapping("/v1/hjwjw/person") public class PersonController { private IPersonService personService; public PersonController (IPersonService personService) { this .personService = personService; } @GetMapping @ProcessResult public ResponseEntity<List<Person>> query () { return ResponseEntity.ok(personService.queryPerson()); } @PostMapping @ProcessResult public ResponseEntity<Person> createPerson (@RequestBody Person personVO) { return ResponseEntity.ok(personService.createPerson(personVO)); } @PutMapping @ProcessResult public ResponseEntity<Person> updatePerson (@RequestBody Person personVO) { return ResponseEntity.ok(personService.updatePerson(personVO)); } @DeleteMapping("/{personId}") public ResponseEntity delPerson (@PathVariable("personId") Long personId) { personService.delPerson(personId); return ResponseEntity.ok(HttpStatus.OK); } }
新建切面 新建一个切面类,在Controller中加了 @ProcessResult 注解的方法,在返回前会进入切面进行处理。
返回的Object需要判断是否为集合,并把其父类字段都需要遍历查找是否有添加@SexValue
注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 @Component @Aspect public class SexAspect { private static final Logger LOGGER = LoggerFactory.getLogger(SexAspect.class); @AfterReturning(value = "@annotation(processResult)",returning = "result") public Object aftreReturning (JoinPoint joinPoint, ProcessResult processResult,Object result) throws IllegalAccessException { LOGGER.info(joinPoint.toString()); LOGGER.info("<===================Aspect======================>" ); LOGGER.info(result.toString()); if (result == null ){ return null ; } if (result instanceof ResponseEntity){ Object body = ((ResponseEntity<?>) result).getBody(); if (body == null ){ return null ; } if (body instanceof Collection ){ for (Object obj : (Collection<?>) body){ processObj(obj); } }else { processObj(body); } }else if (result instanceof Collection){ for (Object obj : (Collection<?>) result){ processObj(obj); } }else { processObj(result); } return result; } private void processObj (Object obj) throws IllegalAccessException { List<Field> fieldList = new ArrayList <>(); Class<?> tempClass = obj.getClass(); while (tempClass != null ){ fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields())); tempClass = tempClass.getSuperclass(); } Field[] fields = new Field [fieldList.size()]; fieldList.toArray(fields); for (Field field : fields){ if (field.isAnnotationPresent(SexValue.class)){ field.setAccessible(true ); String fieldValue = String.valueOf(field.get(obj)); LOGGER.info("fieldValue:{}" ,fieldValue); if ("M" .equals(fieldValue)){ field.set(obj,"男" ); }else { field.set(obj,"女" ); } } } } }
这里只是做了简单的值转换。至于如何获取到对应的含义,建议把配置的值集缓存到Redis,这样在切面处理时可以根据编码从 Redis 中直接取出含义进行替换。