1 首页菜单动态化实现
1.1 背景
用户希望在登陆以后基于权限的不同,在首页左侧呈现不同的菜单,如何实现呢?
1.2 初步分析
基于登陆用户id,查询用户对应的菜单信息然后进行呈现
1.3 原理应用分析
方案实现上可以基于同步或异步查询然后进行菜单数据呈现。
1.4 最终解决方案
用户登陆以后,基于用户登陆id查询用户对应的一级菜单,二级菜单然后存储到指定 作用域,当进入系统首页后基于thymeleaf呈现用户菜单。
1.4.1 Pojo类的定义
基于用户需求将查询到的一级菜单以及一级菜单对应的二级菜单查询出来,并进行 封装。
package com.cy.pj.sys.pojo;
@Setter
@Getter
@ToString
public class SysUserMenu implements Serializable{
private static final long serialVersionUID = -8126757329276920059L;
private Integer id; private String name;
private String url; private List<SysUserMenu> childs;
}
1.4.2 Dao接口实现
在SysMenuDao中添加方法,基于菜单id获取所有菜单相关信息,代码如下
/**
* 基于菜单获取菜单信息
* @param menuIds
* * @return */
List<SysUserMenu> findMenusByIds( @Param("menuIds")List<Integer> menuIds);
1.4.3 Mapper元素定义
在SysMenuMapper.xml文件中添加如下映射元素:
<select id="findMenusByIds" resultMap="sysUserMenu">
select p.id,p.name,p.url,c.id cid,c.name cname,c.url curl
from sys_menus p left join sys_menus c
on p.id=c.parentId
where p.parentId is null and c.id in
<foreach collection="menuIds" open="(" close=")" item="menuId" separator=",">
#{menuId}
</foreach>
</select>
<resultMap id="sysUserMenu" type="com.cy.pj.sys.pojo.SysUserMenu">
<!-- 一级菜单映射 -->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="url" column="url"/>
<!-- 二级菜单映射 -->
<collection property="childs" ofType="com.cy.pj.sys.pojo.SysUserMenu">
<id property="id" column="cid"/>
<result property="name" column="cname"/>
<result property="url" column="curl"/>
</collection>
</resultMap>
1.4.4 Service接口及实现
在SysMenuService接口及实现类中定义如下方法,基于用户id查询用户对应的菜单信息, 代码如下
@Override
public List<SysUserMenu> findUserMenusByUserId(Integer id) {
//1.对用户id进行判断
//2.基于用户id查找用户对应的角色id
List<Integer> roleIds= sysUserRoleDao.findRoleIdsByUserId(id);
//3.基于角色id获取角色对应的菜单信息,并进行封装.
List<Integer> menuIds= sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
//4.基于菜单id获取用户对应的菜单信息并返回
return sysMenuDao.findMenusByIds(menuIds);
}
1.4.5 Controller类实现
修改 PageController中的doIndexUI方法
@RequestMapping("doIndexUI")
public String doIndexUI(Model model) {
SysUser user=ShiroUtils.getUser ();
model.addAttribute("username", user.getUsername());
List<SysUserMenu> userMenus= sysUserService.findUserMenusByUserId(user.getId());
model.addAttribute("userMenus",userMenus);
return "starter";
}
1.4.6 Starter页面实现
将如下代码替换starter.html中Sidebar Menu对应的位置。
<ul class="sidebar-menu" data-widget="tree" >
<li class="header" >HEADER</li>
<li class="treeview" th:each="m:${userMenus}" >
<a href="#" ><i class="fa fa-link" ></i>
<span>[[${m.name}]]</span>
<span class="pull-right-container" >
<i class="fa fa-angle-left pull-right" ></i>
</span>
</a>
<ul class="treeview-menu" >
<li th:each="c:${m.childs}" >
<a th:onclick="javascript:doLoadRS([[${c.url}]])" >[[${c.name}]]</a>
</li>
</ul>
</li>
</ul>
当点击对应的菜单元素时,其事件处理函数如下:
<script type="text/javascript">
function doLoadRS(url){
$("#mainContentId").load(url);
}
</script>
2 控制层访问拦截实现
2.1 背景
近项目业务上有新的需求,要求系统登陆操作要有时间限制。
2.2 初步分析
对于类似需求的实现,可采用过滤器(Filter),SpringMVC拦截器,AOP等进行实 现。对于过滤器而言一般主要应用在项目中共性的过滤,AOP需要依托于动态代理以及切 面对象,这样性能方面相对较差,所以终选择使用Spring MVC拦截器进行实现。
2.3 原理应用分析
Spring MVC中的拦截器基于回调机制,可以在目标方法执行之前,先进行业务检测,
满足条件则放行,不满足条件则进行拦截,拦截器原理分析如下图所示:
2.4 最终解决方案实现
第一步:拦截器定义,关键代码如下:
package com.cy.pj.common.web;
/**
* Spring MVC中拦截器
* * @author Administrator */
public class TimeAccessInterceptor implements HandlerInterceptor {
/** * preHandle 在控制层目标方法执行之前执行 */
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandler()");
//获取java中的日历对象
Calendar c=Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 6);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
long start=c.getTimeInMillis();
c.set(Calendar.HOUR_OF_DAY,23);
long end=c.getTimeInMillis();
long cTime=System.currentTimeMillis();
if(cTime<start||cTime>end) throw new ServiceException("不在访问时间之内");
return true;
}
}
第二步:拦截器配置,注册拦截器,关键代码如下
package com.cy.pj.common.config;
@Configuration
public class SpringWebConfig implements WebMvcConfigurer{
//web.xml
//配置spring mvc 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimeAccessInterceptor()).addPathPatterns("/user/doLogin");
}
}
3 项目打包策略及实现
3.1 jar包方式实现(推荐)
假如使用的是sts,可直接对项目进行maven install操作,然后在项目target目录中找到对应的jar包,在当前目录下执行java -jar project-a.jar即可(假如 project-a.jar为打包好的jar文件)
说 明:可以在打包时跳过测试类,打开pom.xml,在properties元素中添加跳过测试 元素( < skip Tests>true</skip Tests>),代码如下:
<properties>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
3.2 war包方式实现(了解)
第一步:修改pom.xml,将打包方式修改为war:
<packaging>war</packaging>
第二步:添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
修改启动类
@SpringBootApplication
public class Application extends SpringBootServletInitializer{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
第三步:对项目进行maven install操作
第四步:将项目target目录下生成的war包拷贝到指定tomcat的webapps目录,启动 tomcat测试运行,
例如 http://localhost:8080/CGB-DB-SYS-V1.04-0.0.1-SNAPSHOT/doIndexUI
4 项目总结
4 .1 知识点总结
- Spring Boot (入门,基础,进阶)
- Thymeleaf&ajax
- dbms(动吧系统中的权限子系统)
- Spring AOP (AspectJ,事务,异步,缓存,日志)
- Shiro (安全框架-认证,授权,会话,记住我,加密)
4.2 项目业务总结
1.项目中各个模块之间的业务关系。
2.项目中各个业务模块的实现过程?(pojo,dao,service,controller)
3.项目中事务的控制,缓存应用,认证和授权核心的业务实现。
4.建议:通读阿里巴巴的开发手册,熟悉其标准。
4.3 项目代码实现总结
项目核心业务流程,如图所示: