Spring Boot自定义注解实现属性增强(含源码)

一.背景

  实际开发场景中,当有涉及到数据码表(数据字典)时,业务数据库表通常存储的是字典值,不是字典描述。所以在实际业务开发当中需要将字典值翻译为字典描述,在代码中每次去单独的遍历十分繁琐,所以这里实现的将字典值翻译为字典描述的动作单独提出到切面,由切面去实现动态翻译和字段查询赋值。

 

二.实现效果

  通过接口查询业务表,系统自动将业务表中的数据翻译为数据字典描述值,并且在接口查询结果中添加描述值

 

 

 

三.实现步骤

  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

 

五.源码

  源码:https://github.com/CodingPandaLLL/tsl.git

posted @ 2022-03-21 17:49  CodingPanda  阅读(2059)  评论(0编辑  收藏  举报