基于Jackson实现动态添加返回字段,并且支持字典转换

可能有很多友友们针对某些下拉框,单选框,多选框,设计表的时候会设计value和Label两个字段。

但考虑到可能字典描述可能会变,又不想查出数据后再去转一次。

这时候可以考虑通过自定义切面的方式,动态添加返回字段。

1、定义自定义注解

@JacksonAnnotationsInside
@JsonSerialize(using = DicTransContextualSerializer.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EmbedTrans {

    /**
     * 转译输出字段,若不配置,会在源字段名后面加Name
     * 如:sex->sexName
     *
     * @return
     */
    String fieldName() default "";

    /**
     * 需要转换的字典类型
     *
     * @return
     */
    SysDictTypeEnum dicMetohd();
}

2、定义序列化类,具体的转换在【DicTransIntegerSerializer.serialize】里面

public class DicTransContextualSerializer extends JsonSerializer<Objects> implements ContextualSerializer {

    private static final String DISPLAY_SUFFIX = "Desc";

    @Autowired
    private DictTranContext dictTranContext;

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            if (Objects.equals(beanProperty.getType().getRawClass(), Integer.class)
                    || Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                EmbedTrans t = beanProperty.getAnnotation(EmbedTrans.class);
                if (t != null) {
                    String beanFieldName = beanProperty.getName();
                    if (StringUtils.hasText(t.fieldName())) {
                        beanFieldName = t.fieldName();
                    } else {
                        beanFieldName = beanProperty.getName() + DISPLAY_SUFFIX;
                    }
                    //SysDictTypeEnum dicTypeEnum = SysDictTypeEnum.NORMAL_CODE_TRANS_NAME;
                    SysDictTypeEnum dicTypeEnum = null;
                    if (t.dicMetohd() != null) {
                        dicTypeEnum = t.dicMetohd();
                    }
                    //这个位置目前测试没啥问题
                    if (Objects.equals(beanProperty.getType().getRawClass(), Integer.class)) {
                        //DicInterface.bel();
                        return new DicTransIntegerSerializer(dictTranContext, beanFieldName, dicTypeEnum);
                    } else {
                        //DicTransStringSerializer
                        return new DicTransIntegerSerializer(dictTranContext, beanFieldName, dicTypeEnum);
                    }
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(beanProperty);
    }

    @Override
    public void serialize(Objects s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

    }
}
public class DicTransIntegerSerializer extends JsonSerializer<Integer> {

    //注入失败
//    @Autowired
//    private DictTranContext dictTranContext;
    //private DictTranContext dictTranContext =
    //            (DictTranContext) SpringUtil.getBean("DictTranContext");


    private DictTranContext dictTranContext;
    private String fieldName;
    private SysDictTypeEnum dicTypeEnum;

    public DicTransIntegerSerializer(DictTranContext dictTranContext, String fieldName, SysDictTypeEnum dicTypeEnum) {
        this.fieldName = fieldName;
        this.dicTypeEnum = dicTypeEnum;
        this.dictTranContext = dictTranContext;
    }

    public DicTransIntegerSerializer() {
    }

    @Override
    public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeObject(integer);
        jsonGenerator.writeFieldName(fieldName);
        //TODO 写自己具体逻辑
        if (ObjectUtil.isNotEmpty(integer)) {
            DictTranTemplate dictTranTemplate = dictTranContext.getTemplateInstance();
            String dictLabel = dictTranTemplate.dictConver(dicTypeEnum, String.valueOf(integer));
            if (ObjectUtil.isNotEmpty(dictLabel)) {
                jsonGenerator.writeString(dictLabel);
                return;
            }
        }
        jsonGenerator.writeString("");
    }
}

3.配置注解,指定转换字段

【@EmbedTrans(dicMetohd = SysDictTypeEnum.COMMON_ENABLE)】

public class SysUserQueryVO {

    @ApiModelProperty("主键ID")
    private Integer id;

    @ApiModelProperty("主键ID")
    private String username;

    @ApiModelProperty("是否启用(0启用,1禁用)")
    //@DictLabel(dictType = SysDictTypeEnum.COMMON_ENABLE)
    @EmbedTrans(dicMetohd = SysDictTypeEnum.COMMON_ENABLE)
    private Integer enable;

    @ApiModelProperty(value = "姓名")
    @SensitiveData(strategy = SensitiveEnum.USERNAME)
    private String nickname;

    @ApiModelProperty(value = "身份证")
    @SensitiveData(strategy = SensitiveEnum.ID_CARD)
    private String idCardNum;

    @ApiModelProperty(value = "联系电话")
    private String mobile;

    @ApiModelProperty(value = "门店id")
    private Integer storeId;
}

正常来说完成上面步骤就可以针对字典值字段动态添加字段描述字段返回给前端。

但是友友们的项目可能微服务架构,数据库db层和逻辑层service不在一个项目中,db无法使用service层的字典转换方法(可能也要查咖啡因一级缓存和redis二级缓存),后面就用了设计模式的思想进一步封装。(可能我实现比较复杂)

4、自定义【DictTranAnno】注解,用在springboot启动时扫描该注解,缓存到map中后续直接使用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DictTranAnno {

    // 转换类型
    String tranType() default "default";
}

5、定义上下文,用来扫描【DictTranAnno】注解

@Slf4j
@Component
public class DictTranContext implements ApplicationContextAware {

    @Resource
    private ApplicationContext applicationContext;

    private static final Map<String, Class<?>> templateMap = new HashMap<>(8);

    public DictTranTemplate getTemplateInstance() {
        Class<?> clazz = templateMap.get("default");
        if (clazz == null) {
            throw new RuntimeException("默认字典转换处理不存在");
        }
        return (DictTranTemplate) this.applicationContext.getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        log.info(">>>>>>>>>>>>>>>>>>>>>>>开始加载字典转换>>>>>>>>>>>>>>>>>>>>>>>");
        // 遍历带有DictTranAnno注释的类
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(DictTranAnno.class);
        if (beans != null && beans.size() > 0) {
            //TODO 判断是否有相同tranType,有的话则抛异常
            for (Object serviceBean : beans.values()) {
                String tranType = serviceBean.getClass().getAnnotation(DictTranAnno.class).tranType();
                templateMap.put(tranType, serviceBean.getClass());
            }
        }
        log.info(">>>>>>>>>>>>>>>>>>>>>>>加载字典转换完成>>>>>>>>>>>>>>>>>>>>>>>");
        log.info(">>>>>>>>>>>>>>>>>>>>>>>" + templateMap.toString() + ">>>>>>>>>>>>>>>>>>>>>>>");
    }
}

6、在DB项目定义抽象类,用来实现具体转换

@Service
public abstract class DictTranTemplate {

    /**
     * 用来转换字典的具体实现
     * @param dicTypeEnum
     * @param dictValue
     * @return
     */
    protected abstract String dictTran(SysDictTypeEnum dicTypeEnum, String dictValue);

    /**
     * 抛出给调用者的方法
     * @param dicTypeEnum
     * @param dictValue
     * @return
     */
    public String dictConver(SysDictTypeEnum dicTypeEnum, String dictValue) {
        return dictTran(dicTypeEnum, dictValue);
    }
}

【DicTransIntegerSerializer.serialize】中具体实现代码为

 7、在Service项目定义实现类继承模板类【DictTranTemplate】

@Service
@DictTranAnno
public class DicTranSerializer extends DictTranTemplate {

    @Autowired
    private ISysDictDataService dataService;

    @Override
    protected String dictTran(SysDictTypeEnum dicTypeEnum, String dictValue) {
        //自己的转换实现
        return dataService.getDictLabelRedisByValue(dicTypeEnum.getCode(), dictValue);
    }
}

至此就完成啦,实现的可能并不是很完美,但也学到了设计模式的使用。

有不清楚的地方欢迎评论~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Spring Boot 中动态修改返回字段名可以通过自定义序列化器来实现。你可以按照以下步骤进行操作: 1. 创建一个自定义的序列化器类,实现 `JsonSerializer` 接口。例如,可以命名为 `DynamicFieldNameSerializer`。 2. 在 `DynamicFieldNameSerializer` 类中重写 `serialize` 方法,对字段名进行动态修改。你可以根据需要编写逻辑来决定如何修改字段名。 3. 在你的实体类中,使用 `@JsonSerialize` 注解来指定使用自定义的序列化器。例如,`@JsonSerialize(using = DynamicFieldNameSerializer.class)`。 以下是一个示例: ```java import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @JsonSerialize(using = DynamicFieldNameSerializer.class) public class MyEntity { private String originalFieldName; // getter and setter } public class DynamicFieldNameSerializer extends JsonSerializer<MyEntity> { @Override public void serialize(MyEntity entity, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); gen.writeStringField("modifiedFieldName", entity.getOriginalFieldName()); // 其他字段序列化逻辑 gen.writeEndObject(); } } ``` 在这个示例中,`DynamicFieldNameSerializer` 类重写了 `serialize` 方法,在生成 JSON 时将 `originalFieldName` 字段以 `modifiedFieldName` 的名字返回。 请注意,以上示例只是一个简单的示例,你可以根据自己的需求在 `DynamicFieldNameSerializer` 类中编写更复杂的逻辑来动态修改返回字段名。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值