服务器测评网
我们一直在努力

首页菜单java源码怎么写

首页菜单作为用户接触系统的第一个界面,其设计直接影响用户体验和操作效率,在Java开发中,实现一个功能完善、结构清晰的首页菜单需要兼顾数据结构设计、业务逻辑处理、权限控制及前端交互等多个环节,本文将从需求分析、设计思路、核心代码实现及优化方向展开,详细拆解首页菜单的Java源码编写方法。

首页菜单java源码怎么写

需求分析与设计思路

在编码前,需明确首页菜单的核心功能需求,首页菜单需满足以下几点:

  1. 多级菜单支持:支持一级、二级甚至三级菜单嵌套,如“系统管理”下包含“用户管理”“角色管理”等子菜单。
  2. 动态权限控制:根据用户角色动态显示菜单,普通用户不可访问管理员专属菜单。
  3. 数据持久化:菜单信息需存储于数据库,支持动态增删改查,避免硬编码。
  4. 前端适配:返回的数据结构需符合前端框架(如Vue、React)的渲染需求,通常为树形结构。

基于上述需求,技术选型上可采用Spring Boot作为后端框架,MyBatis-Plus操作数据库,Redis缓存菜单数据提升性能,Spring Security实现权限控制,数据结构设计上,菜单表需包含核心字段:菜单ID(menu_id)、父菜单ID(parent_id)、菜单名称(menu_name)、路径(path)、图标(icon)、排序值(order_num)、权限标识(permission)、是否可见(visible)等,通过parent_id构建树形层级关系。

核心代码实现

实体类设计

首先定义菜单实体类SysMenu,对应数据库表sys_menu,使用Lombok简化代码:

@Data
@TableName("sys_menu")
public class SysMenu {
    @TableId(type = IdType.AUTO)
    private Long menuId;
    private String menuName;
    private Long parentId;
    private String path;
    private String component;
    private String icon;
    private Integer orderNum;
    private String permission;
    private Boolean visible;
    private Boolean status;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

注:component字段用于前端路由组件映射,若为非路由类型(如按钮)可设为空。

Mapper层实现

菜单数据的核心操作是树形结构查询,需实现“根据父ID查询子菜单”和“构建完整树”的方法,使用MyBatis-Plus的BaseMapper扩展:

public interface MenuMapper extends BaseMapper<SysMenu> {
    // 查询用户可见的菜单列表(含权限过滤)
    @Select("SELECT * FROM sys_menu WHERE visible = 1 AND status = 1 " +
            "AND (permission IS NULL OR permission IN " +
            "(SELECT DISTINCT permission FROM sys_role_menu WHERE role_id IN " +
            "(SELECT role_id FROM sys_user_role WHERE user_id = #{userId}))) " +
            "ORDER BY order_num")
    List<SysMenu> selectVisibleMenusByUserId(Long userId);
}

通过SQL关联用户角色、角色菜单表,过滤出当前用户有权限的菜单。

Service层实现

Service层负责处理业务逻辑,核心是“将扁平化菜单列表转换为树形结构”,采用递归或Stream API实现:

首页菜单java源码怎么写

@Service
public class MenuServiceImpl implements MenuService {
    @Autowired
    private MenuMapper menuMapper;
    @Override
    public List<MenuTreeVO> buildMenuTree(Long userId) {
        List<SysMenu> menuList = menuMapper.selectVisibleMenusByUserId(userId);
        List<MenuTreeVO> treeList = new ArrayList<>();
        // 扁平数据转树形:递归处理
        menuList.forEach(menu -> {
            if (menu.getParentId() == 0L) {
                MenuTreeVO treeVO = convertToTreeVO(menu);
                treeList.add(treeVO);
            }
        });
        return treeList;
    }
    private MenuTreeVO convertToTreeVO(SysMenu menu) {
        MenuTreeVO treeVO = new MenuTreeVO();
        BeanUtils.copyProperties(menu, treeVO);
        List<MenuTreeVO> children = new ArrayList<>();
        menuList.forEach(child -> {
            if (child.getParentId().equals(menu.getMenuId())) {
                children.add(convertToTreeVO(child));
            }
        });
        treeVO.setChildren(children);
        return treeVO;
    }
}

其中MenuTreeVO是前端适配的VO类,包含children字段用于存储子菜单:

@Data
public class MenuTreeVO {
    private Long menuId;
    private String menuName;
    private String path;
    private String icon;
    private Integer orderNum;
    private List<MenuTreeVO> children;
}

Controller层实现

Controller层提供RESTful接口,供前端获取菜单数据:

@RestController
@RequestMapping("/menu")
public class MenuController {
    @Autowired
    private MenuService menuService;
    @GetMapping("/tree")
    public Result<List<MenuTreeVO>> getMenus(@RequestParam Long userId) {
        List<MenuTreeVO> menuTree = menuService.buildMenuTree(userId);
        return Result.success(menuTree);
    }
}

返回结果封装为统一响应格式Result,包含状态码和数据。

优化与扩展方向

缓存优化

菜单数据变更频率较低,可通过Redis缓存减少数据库查询,在Service层添加缓存逻辑:

@Cacheable(value = "menuCache", key = "#userId")
public List<MenuTreeVO> buildMenuTree(Long userId) {
    // 原有逻辑
}

使用@Cacheable注解,缓存键为用户ID,菜单数据更新时通过@CacheEvict清除缓存。

动态权限控制

若菜单需实时响应权限变更(如角色分配后立即生效),可结合Spring Security的@PreAuthorize注解,在Controller层添加权限校验:

@GetMapping("/tree")
@PreAuthorize("hasAuthority('system:menu:list')")
public Result<List<MenuTreeVO>> getMenus(@RequestParam Long userId) {
    // 接口逻辑
}

确保只有拥有system:menu:list权限的用户才能调用菜单接口。

首页菜单java源码怎么写

前端交互优化

前端可通过递归组件渲染树形菜单,例如Vue中的MenuTree.vue

<template>
  <el-submenu v-if="item.children && item.children.length > 0" :index="item.path">
    <template #title>
      <i :class="item.icon"></i>
      <span>{{ item.menuName }}</span>
    </template>
    <menu-tree v-for="child in item.children" :key="child.menuId" :item="child" />
  </el-submenu>
  <el-menu-item v-else :index="item.path">
    <i :class="item.icon"></i>
    <span>{{ item.menuName }}</span>
  </el-menu-item>
</template>
<script>
export default {
  name: 'MenuTree',
  props: {
    item: {
      type: Object,
      required: true
    }
  }
}
</script>

后端返回的树形数据可直接驱动组件渲染,支持多级嵌套和图标显示。

性能优化

当菜单数据量较大时(如上千条),递归构建树可能导致性能问题,可改用“空间换时间”的方式,通过Map存储节点引用,一次遍历完成树构建:

public List<MenuTreeVO> buildMenuTreeOptimized(List<SysMenu> menuList) {
    Map<Long, MenuTreeVO> menuMap = new HashMap<>();
    List<MenuTreeVO> rootList = new ArrayList<>();
    // 第一次遍历:存入Map
    menuList.forEach(menu -> {
        MenuTreeVO treeVO = new MenuTreeVO();
        BeanUtils.copyProperties(menu, treeVO);
        menuMap.put(menu.getMenuId(), treeVO);
    });
    // 第二次遍历:构建父子关系
    menuList.forEach(menu -> {
        MenuTreeVO treeVO = menuMap.get(menu.getMenuId());
        if (menu.getParentId() == 0L) {
            rootList.add(treeVO);
        } else {
            MenuTreeVO parent = menuMap.get(menu.getParentId());
            if (parent != null) {
                parent.getChildren().add(treeVO);
            }
        }
    });
    return rootList;
}

此方法时间复杂度为O(n),适合大数据量场景。

首页菜单的Java实现需从数据结构、业务逻辑、权限控制及性能优化多维度设计,通过树形结构存储菜单数据、递归或Map优化构建树、结合缓存与权限控制,可开发出功能完善、性能良好的菜单系统,实际开发中还需根据业务需求灵活调整,如添加菜单按钮权限、国际化支持等,确保系统可扩展性与用户体验。

赞(0)
未经允许不得转载:好主机测评网 » 首页菜单java源码怎么写