一.背景
實際開發場景中,當有涉及到數據碼表(數據字典)時,業務數據庫表通常存儲的是字典值,不是字典描述。所以在實際業務開發當中需要將字典值翻譯為字典描述,在代碼中每次去單獨的遍歷十分繁瑣,所以這里實現的將字典值翻譯為字典描述的動作單獨提出到切面,由切面去實現動態翻譯和字段查詢賦值。
二.實現效果
通過接口查詢業務表,系統自動將業務表中的數據翻譯為數據字典描述值,並且在接口查詢結果中添加描述值
三.實現步驟
1.添加自定義注解
package com.cpl.tsl.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義注解 * * @author: lll * @date: 2022年03月21日 17:03:08 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) @Documented public @interface DataDict { /** * 分類 * * @return 分類 */ String type(); }
2.添加攔截器,攔截請求結果進行解析
package com.cpl.tsl.listener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cpl.tsl.bean.ResultMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 屬性增強攔截器
*
* @author: lll
* @date: 2022年03月20日 07:03:34
*/
@Component("dataDictionary")
@Aspect
public class DataDictionaryListener {
private static final Logger logger = LoggerFactory.getLogger(DataDictionaryListener.class);
//返回對象屬性值名稱
private String MESSAGE = "message";
private String STATUS = "status";
private String DATA = "data";
//攔截解析結果類
private String resultMapName = "com.cpl.tsl.bean.ResultMap";
//表示這個包下面的類才有效
private static final String NEED_SCAN_PACKAGE = "com.cpl.tsl.bean";
/**
* 在請求之中攔截獲取攔截
*/
@Around("execution(* com.cpl.tsl.controller..*.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
// result的值就是被攔截方法的返回值
Object result = pjp.proceed();
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
// 被切的方法
Method method = methodSignature.getMethod();
// 返回類型
Class<?> methodReturnType = method.getReturnType();
if (!resultMapName.equals(methodReturnType.getName())) {
return result;
}
// 實例化
ResultMap resultMap = new ResultMap();
Field[] fieldInfo = methodReturnType.getDeclaredFields();
for (Field field : fieldInfo) {
field.setAccessible(true);
if (MESSAGE.equals(field.getName()) && field.get(result) != null) {
resultMap.setMessage(field.get(result).toString());
}
if (STATUS.equals(field.getName()) && field.get(result) != null) {
resultMap.setStatus(field.get(result).toString());
}
if (DATA.equals(field.getName()) && field.get(result) != null) {
logger.info(field.get(result).getClass().getName());
DataDictSerializeFilter dataDictSerializeFilter = new DataDictSerializeFilter();
// list,特殊處理一下
if (field.get(result) instanceof List) {
List list = (List) field.get(result);
List resultList = new ArrayList();
for (Object o : list) {
JSONObject resultJson = writeFieldToObject(dataDictSerializeFilter, o);
resultList.add(resultJson);
}
resultMap.setData(resultList);
} else {
JSONObject resultJson = writeFieldToObject(dataDictSerializeFilter, field.get(result));
resultMap.setData(resultJson);
}
}
}
return resultMap;
}
private JSONObject writeFieldToObject(DataDictSerializeFilter dataDictSerializeFilter, Object result) throws IllegalAccessException {
String dataString = JSON.toJSONString(result, dataDictSerializeFilter);
JSONObject resultJson = JSONObject.parseObject(dataString);
Field[] fields = result.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
//field Name
String fieldName = fields[i].getName();
String packageName = fields[i].getClass().getPackage().getName();
//include other bean
if (packageName.startsWith(NEED_SCAN_PACKAGE)) {
String sonJsonString = JSON.toJSONString(fields[i].get(result), dataDictSerializeFilter);
JSONObject sonResultJson = JSONObject.parseObject(sonJsonString);
resultJson.put(fieldName, sonResultJson);
}
//include list
fields[i].setAccessible(true);
if (fields[i].get(result) instanceof List) {
List list = (List) fields[i].get(result);
List resultList = new ArrayList();
for (Object o : list) {
String sonListJsonString = JSON.toJSONString(o, dataDictSerializeFilter);
JSONObject sonListResultJson = JSONObject.parseObject(sonListJsonString);
resultList.add(sonListResultJson);
}
resultJson.put(fieldName, resultList);
}
}
return resultJson;
}
}
3.添加過濾器,屬性翻譯並添加字段
package com.cpl.tsl.listener; import com.alibaba.fastjson.serializer.AfterFilter; import com.cpl.tsl.annotation.DataDict; import com.cpl.tsl.bean.SysDataDict; import com.cpl.tsl.service.Impl.SysDataDictServiceImpl; import com.cpl.tsl.utils.SpringUtil; import org.springframework.util.StringUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Field; /** * 序列化屬性增強 * * @author: lll * @date: 2022年03月20日 07:03:09 */ public class DataDictSerializeFilter extends AfterFilter { /** * 表示這個包下面的類才有效 */ private static final String NEED_SCAN_PACKAGE = "com.cpl.tsl.bean"; private static final String DISPLAY_SUFFIX = "Description"; private SysDataDictServiceImpl sysDataDictServiceImpl = (SysDataDictServiceImpl) SpringUtil.getBean("sysDataDictServiceImpl"); @Override public void writeAfter(Object object) { String packageName = object.getClass().getPackage().getName();//該方法是獲取包名,可以利用該方法的結果來縮小范圍 if (!packageName.startsWith(NEED_SCAN_PACKAGE)) { return; } //獲取所有的字段 Field[] fields = object.getClass().getDeclaredFields(); //遍歷字段 try { for (Field f : fields ) { //獲取字段上的自定義注解 Annotation annotation = f.getAnnotation(DataDict.class); if (annotation == null) { continue; } //獲取DataDict類型 String type = f.getAnnotation(DataDict.class).type(); if (StringUtils.isEmpty(type)) { continue; } f.setAccessible(true); //獲取屬性值 String o = f.get(object).toString(); if (StringUtils.isEmpty(o)) { continue; } //獲取字典值 SysDataDict sysDataDict = sysDataDictServiceImpl.getSysDataDictByCodeAndFCode(o, type); if (sysDataDict != null && !StringUtils.isEmpty(sysDataDict.getDesp())) { //構造一個新的key value super.writeKeyValue(f.getName() + DISPLAY_SUFFIX, sysDataDict.getDesp()); } } } catch (IllegalAccessException e) { e.printStackTrace(); } } }
4.添加工具類
package com.cpl.tsl.utils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * 手動注入service工具類 * * @author: lll * @date: 2022年03月21日 16:03:42 */ @Component public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null; public SpringUtil() { } public void setApplicationContext(ApplicationContext arg0) throws BeansException { if (applicationContext == null) { applicationContext = arg0; } } public static ApplicationContext getApplicationContext() { return applicationContext; } public static void setAppCtx(ApplicationContext webAppCtx) { if (webAppCtx != null) { applicationContext = webAppCtx; } } /** * 拿到ApplicationContext對象實例后就可以手動獲取Bean的注入實例對象 */ public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } public static <T> T getBean(String name, Class<T> clazz) throws ClassNotFoundException { return getApplicationContext().getBean(name, clazz); } public static final Object getBean(String beanName) { return getApplicationContext().getBean(beanName); } public static final Object getBean(String beanName, String className) throws ClassNotFoundException { Class clz = Class.forName(className); return getApplicationContext().getBean(beanName, clz.getClass()); } public static boolean containsBean(String name) { return getApplicationContext().containsBean(name); } public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return getApplicationContext().isSingleton(name); } public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { return getApplicationContext().getType(name); } public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return getApplicationContext().getAliases(name); } }
5.在實體類的字段上添加自定義的注解 @DataDict(type = "SEX")
package com.cpl.tsl.bean; import com.cpl.tsl.annotation.DataDict; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; @ApiModel(value = "Employee", description = "Employee實體類") public class Employee implements Serializable { @ApiModelProperty(value = "id") private Integer id; @ApiModelProperty(value = "姓名") private String lastName; @DataDict(type = "SEX") @ApiModelProperty(value = "性別") private Integer gender; @ApiModelProperty(value = "郵箱") private String email; @ApiModelProperty(value = "父級id") private Integer dId; public void setId(Integer id) { this.id = id; } public void setLastName(String lastName) { this.lastName = lastName; } public void setGender(Integer gender) { this.gender = gender; } public void setEmail(String email) { this.email = email; } public void setdId(Integer dId) { this.dId = dId; } public Integer getId() { return id; } public String getLastName() { return lastName; } public Integer getGender() { return gender; } public String getEmail() { return email; } public Integer getdId() { return dId; } }
四.注意事項
1.目前代碼只能對返回結果類型為ResultMap的接口進行解析,其他類型直接跳過
2.需要將實際實體類屬性賦值給ResultMap中的data
五.源碼