Java中實現樹形菜單的兩種方式
一、什么是目錄結構?
就是在實際開發(fā)過程中,總會遇到菜單,或則是權限,這個時候就涉及到后端返回數據給前端的時候,不能一個集合把數據一股腦的全部扔給前端,總要把數據整理好,做成像書目錄一樣的結構返回給前端。就像以下圖示一樣
二、目錄樹結構實現寫法
1、準備階段
①創(chuàng)建數據表
PS:如果是練習可以不用創(chuàng)建數據庫,數據全部通過java代碼來創(chuàng)建也可以
CREATE TABLE permission_directory ( id int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', parent_id int(11) NOT NULL DEFAULT '0' COMMENT '父目錄ID', menu_name varchar(255) NOT NULL COMMENT '菜單名稱', menu_level int(11) NOT NULL COMMENT '菜單等級', route varchar(255) NOT NULL COMMENT '路由', PRIMARY KEY (id) COMMENT '主鍵', UNIQUE KEY parent_id (parent_id,menu_name,menu_level,route) COMMENT '唯一索引,包含父目錄ID、菜單名稱、菜單等級和路由' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '存儲引擎為InnoDB,字符集為utf8';
②向表中插入數據
INSERT INTO permission_directory (parent_id, menu_name, menu_level, route) VALUES (1, '首頁', 0, '/index'), (2, '系統(tǒng)設置', 0, '/user/manage'), (3, '操作手冊', 0, '/role/manage'), (4, '菜單管理', 2, '/menu/manage'), (5, '用戶管理', 2, '/system/setting'), (6, '日志管理', 3, '/log/manage'), (7, '定時任務', 3, '/task/schedule'), (8, 'API接口文檔', 3, '/api/documentation'), (9, '操作手冊', 8, '/operation/manual');
③創(chuàng)建菜單對象PermissionDirectory類
PS:這里我用了@Data注解,就不用封裝屬性了,如果沒寫@Data注解就把每個屬性封裝以下,也就是get()和set()方法
@Data public class PermissionDirectory { @MyAnnotation("主鍵id") private int id; @MyAnnotation("父目錄id") private int parentId; @MyAnnotation("菜單名稱") private String menuName; @MyAnnotation("菜單等級") private int menuLevel; @MyAnnotation("路由") private String route; }
④創(chuàng)建存儲菜單對象PermissionDirectoryResVO類
@Data public class PermissionDirectoryResVO { @MyAnnotation("主鍵id") private Integer id; @MyAnnotation("父目錄id") private Integer parentId; @MyAnnotation("菜單名稱") private String menuName; @MyAnnotation("菜單等級") private Integer menuLevel; @MyAnnotation("路由") private String route; @MyAnnotation("用于存儲當前目錄下面的全部子集") private List<PermissionDirectoryResVO> authMenuList; }
2、邏輯代碼實現
這里關于如何去連接數據庫啊等等一系列都省略了,關鍵就是目錄樹的邏輯講解
①第一種寫法
public List<PermissionDirectoryResVO> searchMenu() { List<PermissionDirectoryResVO> directoryTree = new ArrayList<>(); List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList(); if (CollectionUtil.isNotEmpty(menuList)){ List<PermissionDirectoryResVO> pdr = menuList.stream().map(PermissionDirectory -> { PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO(); BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO); return permissionDirectoryResVO; }).collect(Collectors.toList()); pdr.forEach(e ->{ List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr); e.setAuthMenuList(pdrList != null ? pdrList : null); }); List<PermissionDirectoryResVO> parentNodes = pdr.stream(). filter(e -> e.getParentId().equals(0)).collect(Collectors.toList()); directoryTree.addAll(parentNodes); } return directoryTree; } /** * 獲取全部子集 * @param id * @param list * @return */ public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){ return list.stream().filter(t-> t.getParentId().equals(id)).collect(Collectors.toList()); } }
第一種寫法代碼詳細解
第一步:創(chuàng)建存儲最終結果數據的集合容器 List<PermissionDirectoryResVO> directoryTree = new ArrayList<>(); 第二步:獲取需要整理成樹狀結構的所有數據 List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList(); PS:這里我是通過查詢數據獲取的數據,練習的話,可以new一些數據出來存入集合中就行了 第三步:判斷獲取的數據是否為空,如果為空的話就沒有去整理成樹結構的必要了,數據都沒有 if (CollectionUtil.isNotEmpty(menuList)){ .... } PS:這里我用的是糊涂類提供的方法進行判斷,如果小白在寫的過程中發(fā)現報錯,找不到這個方法或則這個類就換一種寫法 第四步:將獲取的PermissionDirectory數據全部賦值給PermissionDirectoryResVO List<PermissionDirectoryResVO> pdr = menuList.stream().map(PermissionDirectory -> { PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO(); BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO); return permissionDirectoryResVO; }).collect(Collectors.toList()); 具體解釋如下: menuList.stream():將menuList集合轉換為一個流(Stream) map(PermissionDirectory -> {...}):這個簡單理解就是循環(huán)menuList集合,然后遍歷集合中的每一個PermissionDirectory元素 BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO):將PermissionDirectory對象的屬性值復制到permissionDirectoryResVO對象中。這樣,authMenuResVO對象就具有了與AuthMenu對象相同的屬性值。 return permissionDirectoryResVO:將轉換后的permissionDirectoryResVO對象作為結果返回給調用者。 collect(Collectors.toList()):將處理后的流中的元素收集到一個新的列表中,并返回該列表 因此,這段代碼的作用是將原始列表menuList中的每個元素轉換為AuthMenuResVO類型的對象,并將轉換后的對象存儲在一個新的列表permissionDirectoryResVO中。 第五步:寫一個獲取子集的方法體 public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){ return list.stream().filter(t-> t.getParentId().equals(id)).collect(Collectors.toList()); } 具體解釋如下: forEach(e -> {...}):是list對象的一個方法,用于遍歷該列表(或集合)中的每個元素,并對每個元素執(zhí)行一段操作。 e -> {...}是一個Lambda表達式,表示對每個元素執(zhí)行的操作,相當于e就是PermissionDirectoryResVO元素對象 因此,這段代碼就是通過傳遞一個主鍵id和一個PermissionDirectoryResVO集合對象參數,然后遍歷循環(huán)PermissionDirectoryResVO對象集合,把每一個對象的父目錄id和傳遞過來的參數id進行對比,如果父目錄id等于參數id就把這個對象收集到新的集合中,最后作為參數返回。 第六步:遍歷全部數據,利用遞歸思想,獲取全部的子集 pdr.forEach(e ->{ List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr); e.setAuthMenuList(pdrList != null ? pdrList : null); }); 具體解釋如下: List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr);這一步通過調用第五步寫好的方法已經獲取到了全部子集,就是說,如果所有數據一集目錄有三個,分別是1、2、3,那么當循環(huán)完的時候會有3個pdrList集合,每個集合中分別裝有1目錄下的數據、2目錄下的數據、3目錄下的數據。 當每一次循環(huán)的時候,都會對pdr集合中的元素進行一次判斷,e.setAuthMenuList(pdrList != null ? pdrList : null);使用三目運算符,如果pdrList集合不為空就表示當前元素有子集,然把pdrList集合賦值給元素的authMenuList屬性,如果為空就表示沒有子集,賦值空就可以。 當集合遍歷完畢,數據情況看圖①實例 第七步:獲取所有頂點數據 List<PermissionDirectoryResVO> parentNodes = pdr.stream(). filter(e -> e.getParentId().equals(0)).collect(Collectors.toList()); directoryTree.addAll(parentNodes); 具體解釋如下: 判斷pdr集合中父目錄id為0的數據,然后賦值給新的parentNodes,最后把這個集合存進directoryTree集合容器中
圖①
②第二種寫法
public List<PermissionDirectoryResVO> searchMenu() { List<PermissionDirectoryResVO> directoryTree = new ArrayList<>(); // 獲取全部數據 List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList(); // 創(chuàng)建存儲PermissionDirectoryResVO對象的集合容器 List<PermissionDirectoryResVO> pdr = new ArrayList<>(); // 判斷集合中數據是否為空,不為空進行樹結構排列 if (CollectionUtil.isNotEmpty(menuList)){ // 遍歷循環(huán)集合menuList元素賦值給pdr集合中元素對象,這里就是第一種寫法的第四步 for (PermissionDirectory permissionDirectory : menuList){ PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO(); permissionDirectoryResVO.setId(permissionDirectory.getId()); permissionDirectoryResVO.setParentId(permissionDirectory.getParentId()); permissionDirectoryResVO.setMenuName(permissionDirectory.getMenuName()); permissionDirectoryResVO.setMenuLevel(permissionDirectory.getMenuLevel()); permissionDirectoryResVO.setRoute(permissionDirectory.getRoute()); pdr.add(permissionDirectoryResVO); } } // 遍歷全部數據,利用遞歸思想,獲取全部的子集,第一種寫法的第六步 for (PermissionDirectoryResVO e : pdr){ List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr); e.setAuthMenuList(pdrList != null ? pdrList : null); } // 獲取所有頂點數據 for (PermissionDirectoryResVO e : pdr){ if (e.getParentId().equals(0)){ directoryTree.add(e); } } return directoryTree; } /** * 獲取全部子集 * @param id * @param list * @return */ public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){ List<PermissionDirectoryResVO> pdr = new ArrayList<>(); // 這里就是第一種寫法的第五步 for (PermissionDirectoryResVO per : list){ if (per.getParentId().equals(id)){ pdr.add(per); } } return pdr; } }
最終結果
{ "code": 200, "msg": "操作成功", "data": [ { "id": 3, "parentId": 0, "menuName": "操作手冊", "menuLevel": 1, "route": "/role/manage", "authMenuList": [ { "id": 8, "parentId": 3, "menuName": "API接口文檔", "menuLevel": 2, "route": "/api/documentation", "authMenuList": [ { "id": 9, "parentId": 8, "menuName": "操作手冊", "menuLevel": 3, "route": "/operation/manual", "authMenuList": [] } ] }, { "id": 7, "parentId": 3, "menuName": "定時任務", "menuLevel": 2, "route": "/task/schedule", "authMenuList": [] }, { "id": 6, "parentId": 3, "menuName": "日志管理", "menuLevel": 2, "route": "/log/manage", "authMenuList": [] } ] }, { "id": 2, "parentId": 0, "menuName": "系統(tǒng)設置", "menuLevel": 1, "route": "/user/manage", "authMenuList": [ { "id": 5, "parentId": 2, "menuName": "用戶管理", "menuLevel": 2, "route": "/system/setting", "authMenuList": [] }, { "id": 4, "parentId": 2, "menuName": "菜單管理", "menuLevel": 2, "route": "/menu/manage", "authMenuList": [] } ] }, { "id": 1, "parentId": 0, "menuName": "首頁", "menuLevel": 1, "route": "/index", "authMenuList": [] } ] }
以上就是Java中實現樹形菜單的兩種方式的詳細內容,更多關于Java實現樹形菜單的資料請關注腳本之家其它相關文章!
相關文章
Spring Boot實戰(zhàn)之逐行釋義Hello World程序
spring boot 是基于Spring的一個框架,Spring boot幫我們集成很多常用的功能,使得整個配置更加簡單。這篇文章主要介紹了Spring Boot實戰(zhàn)之逐行釋義Hello World,需要的朋友可以參考下2017-12-12