SpringBoot消息转换器,Jackson序列化实体过程,重写小小的get()方法,大大的用途

问题背景:业务需求要求,配置树状菜单权限,每个菜单下面有(子菜单树,子按钮数据,子标签树,子数据权限树),正常通过递归list数据可以把子集放到subList里面。
进一步需求:通过每个节点下面的subList,根据类型拆分为

private List<SysResourcesPojoNew> subs;
private List<SysResourcesPojoNew> menusList;
private List<SysResourcesPojoNew> buttonList;
private List<SysResourcesPojoNew> labList;
private List<SysResourcesPojoNew> dataList;

实现操作

1.list 转 Tree

package cn.gewut.base.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * list 转 tree
 * </p>
 *
 * @author yin
 * @since 2023-07-18
 */
public class ListToTreeUtil {


    private String PARENT_FILED_NAME = "parentId" ;
    private String CHILD_FILED_NAME = "childList" ;
    private String ID_FILED_NAME = "id" ;


    public  <T> List<T> transferListToTreeR(List<T> tList, String idFiledName, String parenIdFiledName, String childFiledName) throws NoSuchFieldException {
        if (CollectionUtils.isEmpty(tList)) {
            return tList;
        }
        if (!ObjectUtils.isEmpty(idFiledName)) {
            this.ID_FILED_NAME = idFiledName;
        }
        if (!ObjectUtils.isEmpty(parenIdFiledName)) {
            this.PARENT_FILED_NAME = parenIdFiledName;
        }
        if (!ObjectUtils.isEmpty(childFiledName)) {
            this.CHILD_FILED_NAME = childFiledName;
        }
        return transferListToTree(tList, ID_FILED_NAME, PARENT_FILED_NAME, CHILD_FILED_NAME);

    }


    /**
     * 列表转树形结构
     *
     * @param tList 转换前的列表
     * @param <T>   范型 T 需要保证转换的类中对应关系 Long类型的 parentId 与指向父级的 id  保证类型中包含 List<T> 的 childList 属性
     * @return 转换后的列表
     */

    public  <T> List<T> transferListToTree(List<T> tList) throws NoSuchFieldException {
        if (CollectionUtils.isEmpty(tList)) {
            return tList;
        }
        return transferListToTree(tList, ID_FILED_NAME, PARENT_FILED_NAME, CHILD_FILED_NAME);

    }

    public  <T> List<T> transferListToTree(List<T> tList, String idFieldName, String pidFieldName, String childListFieldName) throws NoSuchFieldException {
        if (CollectionUtils.isEmpty(tList)) {
            return tList;
        }
        if (StringUtils.isEmpty(idFieldName) || StringUtils.isEmpty(pidFieldName) || StringUtils.isEmpty(childListFieldName)) {
            throw new NoSuchFieldException("属性名称缺失");
        }
        return tList.stream().filter(item -> {
            try {
                // 获取父级节点
                Field field = item.getClass().getDeclaredField(pidFieldName);
                field.setAccessible(Boolean.TRUE);
                Long pid = (Long) field.get(item);

                return  null == pid;
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
                throw new RuntimeException("树形结构转换失败");
            }
        }).map(item -> {
            try {
                //处理最高级树
                Field childField = item.getClass().getDeclaredField(childListFieldName);
                childField.setAccessible(Boolean.TRUE);
                childField.set(item, getChildList(item, tList, idFieldName, pidFieldName, childListFieldName));
                return item;
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("树形结构转换失败");
            }
        }).collect(Collectors.toList());
    }

    private  <T> List<T> getChildList(T t, List<T> tList, String idFieldName, String pidFieldName, String childListFieldName) throws NoSuchFieldException, IllegalAccessException {
        Field parentField = t.getClass().getDeclaredField(idFieldName);
        parentField.setAccessible(Boolean.TRUE);
        Long id = (Long) parentField.get(t);
        return tList.stream().filter(item -> {
            try {
                // 获取该节点下的所有子节点
                Field field = item.getClass().getDeclaredField(pidFieldName);
                field.setAccessible(Boolean.TRUE);
                Long pid = (Long) field.get(item)==null?0:(Long) field.get(item);

                return pid.equals(id);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("树形结构转换失败");
            }
        }).peek(item -> {
            try {
                Field childField = item.getClass().getDeclaredField(childListFieldName);
                childField.setAccessible(Boolean.TRUE);
                childField.set(item, getChildList(item, tList, idFieldName, pidFieldName, childListFieldName));
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("树形结构转换失败");
            }
        }).collect(Collectors.toList());
    }


}

2.重写实体类多个get方法,拆分子树


```java
private List<SysResourcesPojoNew> subs;

private List<SysResourcesPojoNew> menusList;

private List<SysResourcesPojoNew> buttonList;

private List<SysResourcesPojoNew> labList;

private List<SysResourcesPojoNew> dataList;


    public List<SysResourcesPojoNew> getMenusList() {

        List<SysResourcesPojoNew> list = new ArrayList<>();
        try {
            if (!ObjectUtils.isEmpty(getSubs())) {
                for (SysResourcesPojoNew sub : getSubs()) {
                    if ("1".equals(sub.getRestype())) {
                        list.add(sub);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(list.size()==0){
            return null;
        }
        return list;
    }


public List<SysResourcesPojoNew> getLabList() {
        List<SysResourcesPojoNew> list = new ArrayList<>();
        try {
            if (!ObjectUtils.isEmpty(getSubs())) {
                for (SysResourcesPojoNew sub : getSubs()) {
                    if ("3".equals(sub.getRestype())) {
                        list.add(sub);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (list.size() == 0) {
            return null;
        }
        return list;
    }

3.Jackson序列化实体过程,返回到前端 ,执行get方法。

背后原理

Jackson序列化实体过程

(参考 https://blog.csdn.net/My__God/article/details/109435313)

1.查找实体类型对应的Srializer,如果尚未创建则先创建一个再返回

Serializer创建过程:
POJOPropertiesCollector

protected void collectAll()
    {
        LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();

        // First: gather basic data
        _addFields(props);
        _addMethods(props);
        // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
        //    inner classes, see [databind#1502]
        if (!_classDef.isNonStaticInnerClass()) {
            _addCreators(props);
        }
        _addInjectables(props);

        // Remove ignored properties, first; this MUST precede annotation merging
        // since logic relies on knowing exactly which accessor has which annotation
        _removeUnwantedProperties(props);
        // and then remove unneeded accessors (wrt read-only, read-write)
        _removeUnwantedAccessor(props);

        // Rename remaining properties
        _renameProperties(props);

        // then merge annotations, to simplify further processing
        // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
        //   annotations from getting properly merged
        for (POJOPropertyBuilder property : props.values()) {
            property.mergeAnnotations(_forSerialization);
        }

        // And use custom naming strategy, if applicable...
        PropertyNamingStrategy naming = _findNamingStrategy();
        if (naming != null) {
            _renameUsing(props, naming);
        }

        /* Sort by visibility (explicit over implicit); drop all but first
         * of member type (getter, setter etc) if there is visibility
         * difference
         */
        for (POJOPropertyBuilder property : props.values()) {
            property.trimByVisibility();
        }

        /* and, if required, apply wrapper name: note, MUST be done after
         * annotations are merged.
         */
        if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
            _renameWithWrappers(props);
        }

        // well, almost last: there's still ordering...
        _sortProperties(props);
        _properties = props;
        _collected = true;
    }

 protected void collectAll() {
        LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();        //props是字段数组,

        _addFields(props);                 // 根据object的字段往 props 数组里赋值, 这个时候是不区分private or public的, 同样也不区分@JsonIgnore注解
        _addMethods(props);                // 根据object的getter方法往 props 数组里赋值
 
        if (!_classDef.isNonStaticInnerClass()) {        //是否静态内部类字段(独有的配置, 在此没有深入研究)
            _addCreators(props);
        }

        _removeUnwantedProperties(props);        //见文知意: 移除unWant的属性, 此时会移除掉@JsonIgnore标注的字段 
                                                 //(@JsonIgnore的作用域是field, Getter, Setter), 即便在序列化时的setter方法上标注,也会去除

        _removeUnwantedAccessor(props);          

        _renameProperties(props);                //属性重命名: (包括json注解 + getter方法覆盖)

        ......

        _sortProperties(props);                //属性排序
        _properties = props;                   //赋值给_properties, _properties会被后面用到
        _collected = true;                        
    }

Serializer主要为一个Property定义列表,其中描述了Propery序列化定义,Property定义列表生成过程为:
1)扫描所有的属性,添加到Property列表中;

2)扫描所有的get方法,根据属性名在property列表中寻找对应的Property定义,如果有对应属性,则将get方法设置为该property的get方法,没有对应属性则根据get方法创建一个property定义;

3)遍历property列表,将标记为ignore的属性从property定义中删除;

4)遍历property列表,将标记为ignore的方法从property定义中删除;

5)遍历property列表,将rename后的属性名添加到property列表中;

2.用创建的Serializer生成对应的json串到指定的writer中

如果实体中指定了策略为@JsonInclude(JsonInclude.Include.NON_DEFAULT),则属性值为默认值的属性将会被忽略,其中属性的默认值获取方式为,调用实体的无参构造函数创建一个实例,调用属性对应的get方法,将其返回值作为属性的默认值,如

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值