利用java的动态调用类和类属性机制, 编写一个工具类来自动为我们创建一个菜单树, 从而使得我们不用每次遇到树结构都要重复的写一些逻辑, 十分不友好.
/**
* 构建树型结构
*
* @author Huzz
* @create 2022-07-25 18:36
*/
public class TreeBuilder<T> {
private static final String SET_METHOD_PREFIX = "set";
private static final String GET_METHOD_PREFIX = "get";
private static final String DEFAULT_ID_NAME = "id";
private static final String DEFAULT_PARENT_ID_NAME = "parentId";
private static final String DEFAULT_CHILDREN_NAME = "children";
/**
* 构建树结构
* @param rootId 根节点id
* @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
* @return
* @throws Exception
*/
public static List builder(String rootId, List dataList) throws Exception {
List tree = new ArrayList<>();
for (Object data : dataList) {
String parentId = data.getClass().getMethod(generateGetMethodName(DEFAULT_PARENT_ID_NAME)).invoke(data).toString();
if (rootId.equals(parentId)) {
// 是根节点的话, 递归查找以该节点id作为parentId的孩子
List children = builder(data.getClass().getMethod(generateGetMethodName(DEFAULT_ID_NAME)).invoke(data).toString(), dataList);
// 设置孩子
data.getClass().getMethod(generateSetMethodName(DEFAULT_CHILDREN_NAME), List.class).invoke(data, children);
// 保存当前节点
tree.add(data);
}
}
return tree;
}
/**
* 构建树结构
* @param rootId 根节点id
* @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
* @param IDFiledName id字段名
* @param parentIdFiledName 父级ID字段名
* @param childrenFiledName 孩子字段名
* @return
* @throws Exception
*/
public static List builder(String rootId, List dataList,String IDFiledName, String parentIdFiledName, String childrenFiledName) throws Exception {
TreeBuilder treeBuilder = new TreeBuilder<>();
return treeBuilder.getTree(rootId, dataList, IDFiledName, parentIdFiledName, childrenFiledName);
}
/**
* 获取树
* @param rootId 根节点id
* @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
* @return
* @throws Exception
*/
public List<T> getTree(String rootId, List<T> dataList) throws Exception {
return builder(rootId, dataList);
}
/**
* 获取树
* @param rootId 根节点id
* @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
* @param IDFiledName id字段名
* @param parentIdFiledName 父级ID字段名
* @param childrenFiledName 孩子字段名
* @return
* @throws Exception
*/
public List<T> getTree(String rootId, List<T> dataList, String IDFiledName, String parentIdFiledName, String childrenFiledName) throws Exception {
List<T> tree = new ArrayList<>();
for (T data : dataList) {
String parentId = data.getClass().getMethod(generateGetMethodName(parentIdFiledName)).invoke(data).toString();
if (rootId.equals(parentId)) {
// 是根节点的话, 查找以该节点id作为parentId的孩子
List<T> children = getTree(data.getClass().getMethod(generateGetMethodName(IDFiledName)).invoke(data).toString(), dataList, IDFiledName, parentIdFiledName, childrenFiledName);
// 设置孩子
data.getClass().getMethod(generateSetMethodName(childrenFiledName), List.class).invoke(data, children);
// 保存当前节点
tree.add(data);
}
}
return tree;
}
private static String generateGetMethodName(String filedName){
return GET_METHOD_PREFIX +filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
}
private static String generateSetMethodName(String filedName){
return SET_METHOD_PREFIX +filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
}
}
测试:
class Test{
public static void main(String[] args) throws Exception {
// 创建一个实体类
@Data
@AllArgsConstructor
class Node {
private String id;
private String data;
private String parentId;
private List<Node> children;
public Node(String id, String data, String parentId) {
this.id = id;
this.data = data;
this.parentId = parentId;
}
}
// 模拟一份数据库Node表
List<Node> nodeList = new ArrayList<>();
nodeList.add(new Node("1", "用户管理", "0"));
nodeList.add(new Node("2", "角色管理", "0"));
nodeList.add(new Node("3", "菜单管理", "0"));
nodeList.add(new Node("4", "工单管理", "0"));
nodeList.add(new Node("5", "登录管理", "0"));
nodeList.add(new Node("6", "日志管理", "0"));
nodeList.add(new Node("7", "用户创建", "1"));
nodeList.add(new Node("8", "用户角色配置", "1"));
nodeList.add(new Node("9", "用户禁用", "1"));
nodeList.add(new Node("10", "角色创建", "2"));
nodeList.add(new Node("11", "角色删除", "2"));
nodeList.add(new Node("12", "角色禁用", "2"));
nodeList.add(new Node("13", "菜单删除", "3"));
nodeList.add(new Node("14", "菜单编辑", "3"));
nodeList.add(new Node("15", "菜单禁用", "3"));
nodeList.add(new Node("16", "创建工单", "4"));
nodeList.add(new Node("17", "修改工单", "4"));
nodeList.add(new Node("18", "登录配置", "5"));
nodeList.add(new Node("19", "登录校验", "5"));
nodeList.add(new Node("20", "系统日志", "6"));
nodeList.add(new Node("21", "登录日志", "6"));
nodeList.add(new Node("22", "普通创建", "7"));
nodeList.add(new Node("23", "特殊创建", "7"));
nodeList.add(new Node("24", "配置A", "8"));
nodeList.add(new Node("25", "配置B", "8"));
nodeList.add(new Node("26", "常规日志", "20"));
nodeList.add(new Node("27", "异常日志", "20"));
// ==== 静态方式调用 ====
List<Node> tree = TreeBuilder.builder("0", nodeList);
// 声明字段名称, 如果自己的实体类与默认的字段名称不一样, 从参数中声明一下
List<Node> tree0 = TreeBuilder.builder("0", nodeList, "id", "parentId", "children");
// ==== 创建对象方式调用 ====
TreeBuilder<Node> builder = new TreeBuilder<>();
List<Node> tree1 = builder.getTree("0", nodeList);
// 声明字段名称, 如果自己的实体类与默认的字段名称不一样, 从参数中声明一下
List<Node> tree2 = builder.getTree("0", nodeList, "id", "parentId", "children");
return;
}
}