AOP+自定义注解+反射自动填充对象枚举属性描述

在开发过程中,根据枚举值设置中文描述的情景非常常见并且重复性极高、代码可读性差(换个人用老实体写新代码可能就要问一遍枚举对应策略),所以想通过自定义注解来实现自动注入。

只需在属性上加入注解@EnumValueAutoAnnotation(enumClass = TipsStatusEnum.class),即可省去重复的set枚举值代码块,一次抒写多次复用。

public class TipsInfoVo implements Serializable {
   
    @ApiModelProperty(value = "状态;(0:未提醒,1:已提醒)")
    @EnumValueAutoAnnotation(enumClass = TipsStatusEnum.class)
    private Integer status;

    @ApiModelProperty(value = "状态-中文描述")
    private String statusName;
		...
}

1. 自定义注解

import com.tips.ai.domain.enums.EnumValueFieldsConstant;
import java.lang.annotation.*;

/**
 * 自定义注解-自动填充枚举描述字段
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface EnumValueAutoAnnotation {

    /**
     * 指定枚举类
     */
    Class<? extends Enum> enumClass();

    /**
     * 自动填充字段名 (暂未扩展)
     */
    String fillFieldName() default Strings.EMPTY;

    /**
     * 映射枚举描述字段名
     */
    EnumValueFieldsConstant autoValueField() default EnumValueFieldsConstant.desc;

}

常量类

import com.tips.ai.config.annotation.EnumValueAutoAnnotation;

/**
 * 自定义注解{@link EnumValueAutoAnnotation}属性 - 表示用来注入值的枚举成员属性
 * 注意:如有枚举属性增加不在此类范围内,要使用自动填充应当在此类中增加成员属性
 * 例如:设置{@link EnumValueAutoAnnotation#autoValueField()} = desc ,那么会寻找到对应枚举并且填充desc字段的值到指定字段
 */
public enum EnumValueFieldsConstant {

	code,
	desc,
	descText;

}

2. aop切面+反射自动填充枚举描述到字段

import com.ruoyi.common.utils.StringUtils;
import com.tips.ai.config.annotation.EnumValueAutoAnnotation;
import com.tips.ai.domain.enums.EnumValueFieldsConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;

/**
 * <p>
 * 自定义切面 - service层查询方法
 * </p>
 *
 * @author lee
 * @implNote 根据指定字段以及该字段上的注解 {@link EnumValueAutoAnnotation} 自动填充枚举描述
 * @since 2022-06-21 22:47:14
 */
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class EnumValueAutofillAspect {

    /**
     * 将所有service层级的查询方法 作为切入点
     */
    @Pointcut("execution(* com.tips.ai.service.*.select*(..)) || execution(* com.tips.ai.service.*.query*(..))")
    public void handlePlaceholder() {
    }

    /**
     * <p>
     * 切面增强具体实现
     * </p>
     * <em>@AfterReturning注解描述</em>
     * <p>
     * @param joinPoint    切面属性
     * @param returnObject 方法返回值
     * @throws Throwable
     */
    @AfterReturning(returning = "returnObject", pointcut = "handlePlaceholder()")
    public void doAfterReturning(JoinPoint joinPoint, Object returnObject) throws Throwable {
        if (returnObject == null) {
            return;
        }
        // 返回值类型为集合类型
        if (returnObject instanceof Collection) {
            Collection ListValue = (Collection) returnObject;
            for (Object v : ListValue) {
                fillEnumNameField(v);
            }
        } else {
            // 返回值类型为单一对象类型
            fillEnumNameField(returnObject);
        }
    }

    /**
     * 填充枚举描述字段
     *
     * @param object 填充对象
     */
    private void fillEnumNameField(Object object) {
        if (object == null) {
            return;
        }
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(object);
                if (value instanceof Collection) {
                    try {
                        Collection collection = (Collection) value;
                        collection.forEach(s -> fillEnumNameField(s));
                    } catch (SecurityException e) {
                        log.error("(Collection) value error: {}", ExceptionUtils.getFullStackTrace(e));
                    }
                }
                if (field.getAnnotations().length < 1 || value == null) {
                    continue;
                }
                EnumValueAutoAnnotation enumValueAutoAnnotation = field.getDeclaredAnnotation(EnumValueAutoAnnotation.class);
                if (enumValueAutoAnnotation == null) {
                    continue;
                }
                log.debug("开始自动填充枚举值 field: {}.{}", object.getClass(), field.getName());
                reflectFindAndFillEnumName(object, field, enumValueAutoAnnotation);
            } catch (Exception e) {
                log.error("enum name auto fill error: {}", ExceptionUtils.getFullStackTrace(e));
            }
        }
    }

    /**
     * 利用反射查找对应枚举项,并设置到对象属性
     *
     * @param object    枚举填充对象
     * @param field     枚举值字段
     * @param enumValueAutoAnnotation
     * @throws Exception
     */
    private void reflectFindAndFillEnumName(Object object, Field field, EnumValueAutoAnnotation enumValueAutoAnnotation) throws Exception {
        Object value = field.get(object);
        Class<? extends Enum> enumClass = enumValueAutoAnnotation.enumClass();
        Method valuesMethod = enumClass.getMethod("values");
        Object[] enums = (Object[]) valuesMethod.invoke(null);
        for (Object e : enums) {
            Field codeField = e.getClass().getDeclaredField(EnumValueFieldsConstant.code.name());
            codeField.setAccessible(true);
            // 取出code对应的枚举
            if (codeField.get(e).equals(value)) {
                Field descTextField = e.getClass().getDeclaredField(enumValueAutoAnnotation.autoValueField().name());
                descTextField.setAccessible(true);
                // 取出对应枚举的value
                Object descTextValue = descTextField.get(e);
                String fillFieldName = StringUtils.isNotBlank(enumValueAutoAnnotation.fillFieldName())
                        ? enumValueAutoAnnotation.fillFieldName() : field.getName() + "Name";
                Optional<Field> fieldOptional = Arrays.stream(object.getClass().getDeclaredFields())
                        .filter(s -> StringUtils.equals(s.getName(), fillFieldName))
                        .findFirst();
                if (!fieldOptional.isPresent()) {
                    log.error("枚举值填充属性{}不存在", fillFieldName);
                    continue;
                }
                Field enumNameField = object.getClass().getDeclaredField(fillFieldName);
                enumNameField.setAccessible(true);
                if (!descTextValue.getClass().equals(enumNameField.getType())) {
                    log.warn("枚举值填充属性{}类型{}无法转换到枚举类型{},请更改为枚举类型一致的类型", fillFieldName, descTextValue.getClass().getSimpleName(), enumNameField.getType().getSimpleName());
                    continue;
                }
                if (enumNameField.get(object) == null) {
                    enumNameField.set(object, descTextValue);
                }
            }
        }
    }

}

3. 在方法返回实体类的属性加上自定义注解

3.1 简约使用方法

public class TipsInfoVo implements Serializable {
   
    @ApiModelProperty(value = "状态;(0:未提醒,1:已提醒)")
    @EnumValueAutoAnnotation(enumClass = TipsStatusEnum.class)
    private Integer status;

    @ApiModelProperty(value = "状态-中文描述")
    private String statusName;
		...
}

3.2 指定填充字段

public class TipsInfoVo implements Serializable {  
  
		@ApiModelProperty(value = "提醒级别-中文描述")
    @EnumValueAutoAnnotation(enumClass = TipsLevelEnum.class, fillFieldName = "tipsLevelStr")
    private Integer tipsLevel;

    @ApiModelProperty(value = "提醒级别;(0:正常,1:重要,2:紧急)")
    private String tipsLevelStr;
  	...
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值