jeesite,其框架主要为:

后端

核心框架:Spring Framework 4.0

安全框架:Apache Shiro 1.2

视图框架:Spring MVC 4.0

服务端验证:Hibernate Validator 5.1

布局框架:SiteMesh 2.4

工作流引擎:Activiti 5.15、FoxBPM 6

任务调度:Spring Task 4.0

持久层框架:MyBatis 3.2

数据库连接池:Alibaba Druid 1.0

缓存框架:Ehcache 2.6、Redis

日志管理:SLF4J 1.7、Log4j

工具类:Apache Commons、Jackson 2.2、Xstream 1.4、Dozer 5.3、POI 3.9

2、前端

JS框架:jQuery 1.9。

CSS框架:Twitter Bootstrap 2.3.1。

客户端验证:JQuery Validation Plugin 1.11。

富文本:CKEcitor

文件管理:CKFinder

动态页签:Jerichotab

手机端框架:Jingle

数据表格:jqGrid

对话框:jQuery jBox

下拉选择框:jQuery Select2

树结构控件:jQuery zTree

日期控件: My97DatePicker

这里对于jeesite,感觉其功能还是挺强大的,但是有一点致命缺点,就是其缓存机制,本来缓存是为了提速,但是,当这里的缓存加上了MVC,并且在前端进行请求后,不适时宜地将请求的相关类对象进行缓存,这就导致了单例化和伪持久化。怎么说来?就是说,当前端修改Person对象实例,并提交到服务端试图保存时,由于某些原因,如权限不足导致保存失败,这本来应该是很正常的,但是,偏偏由于在这之前,缓存将Person对象实例更新了,从而缓存中的该实例是修改后的,这样,后来再次获取该对象,由于缓存存在,优先取缓存而不是从DB里获取,导致,后来获取的对象的数据都是错误的(修改但保存失败的),这就变相单例化,而且是无法获得正确数据了。

例如如下的接口

 @RequiresPermissions("sys:user:edit")
@RequestMapping(value = "save")
public String save(User user, HttpServletRequest request, Model model, RedirectAttributes redirectAttributes) { //判断是否有权限修改用户信息 //先清缓存:因为框架原因,只要更新了该用户,则会同步更新该用户缓存,从而无法获得真正的该用户信息,所以需要清除掉该缓存,这里先注释掉,看问题
//UserUtils.clearCache(user);
User oldUser = systemService.getUser(user.getId());
List<String>roleIdListOld = oldUser.getRoleIdList();
User operator = UserUtils.getUser();
List<String>roleIdListOperator = operator.getRoleIdList();
//自己不能修改自己的权限
// if(user.getId().equals(operator.getId())){
// addMessage(model, "修改用户信息失败, 不能修改自己的权限");
// UserUtils.clearCache();
// return form(oldUser, model);
// }
if(!roleIdListOperator.containsAll(roleIdListOld)){
addMessage(model, "修改用户信息失败, 您的权限不足");
UserUtils.clearCache();
return form(oldUser, model);
}
user.setRoleList(roleList);
// 保存用户信息
systemService.saveUser(user);
// 清除当前用户缓存
if (user.getPhone().equals(UserUtils.getUser().getPhone())){
UserUtils.clearCache();
//UserUtils.getCacheMap().clear();
}
addMessage(redirectAttributes, "保存用户'" + user.getPhone() + "'成功");
return "redirect:" + adminPath + "/sys/user/list?repage";
}

再看下getUser:

 public static User getUser(String id){
User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_ID_ + id);
if (user == null){
user = userDao.get(id);
if (user == null){
return null;
}
user.setRoleList(roleDao.findList(new Role(user)));
CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getPhone(), user);
}
return user;
}

这里的

 systemService.getUser(user.getId());

会一直拿到该对象实例的缓存值,而该值,在修改提交到服务端时,框架已经更新了,再进到controller中。

所以,即使在

 if(!roleIdListOperator.containsAll(roleIdListOld)){
addMessage(model, "修改用户信息失败, 您的权限不足");
UserUtils.clearCache();
return form(oldUser, model);
}

这里返回了,其他地方获取该user的值

 getUser(user.getId());

还是会是缓存的值。
也相当于单例的、全局的实例值

解决方法:

在关系到修改等的地方,每次都需要对该实例进行缓存的清空。同时,在修改时,修改对象最好就是拿出db的该记录,逐个参数进行修改替换:

 @RequiresPermissions("user:list:edit")
@RequestMapping(value = "editUserInfoSave")
public String editUserInfoSave(User user,Model model, RedirectAttributes redirectAttributes) { //先清除该user的缓存,防止干扰到其他地方的引用。其实还是会有并发问题,会在清除之前被引用到
UserUtils.clearCache(user);
//从db中获取user,注意这个userSave 是修改前的,与user的值不一样,注意一点:如果直接从getUser(user.getId());中获取,同时并没有清缓存的前提下
//UserUtils.clearCache(user);则会导致拿到的user并非DB里的user,而是缓存前端提交的
User userSave = systemService.getUserFromDB(user.getId());
/**
* 替换更新修改信息
*/
userSave.setName(user.getName());
userSave.setFirstnameStr(user.getFirstnameStr());
userSave.setLastnameStr(user.getLastnameStr());
userSave.setIdStr(user.getIdStr());
userSave.setUsername(user.getUsername());
userSave.setBirthdateStr(user.getBirthdateStr());
userSave.setEmail(user.getEmail());
userSave.setUserType(user.getUserType());
userSave.setGenderStr(user.getGenderStr());
// 保存用户信息
systemService.saveUser(userSave);
addMessage(redirectAttributes, "保存用户'" + user.getPhone() + "'成功");
return "redirect:" + adminPath + "/user/user/list?repage";
}

这里的getUserFromDB:

 /**
* 根据ID获取用户——通过DB
* @param id
* @return 取不到返回null
*/
public static User getUserFromDB(String id){ User user = userDao.get(id);
user.setRoleList(roleDao.findList(new Role(user)));
return user;
}

因此特别需要注意缓存的使用,不是任何地方都适合使用缓存。

关于jeesite的陷阱需要注意的更多相关文章

  1. 你可能不知道的陷阱, IEnumerable接口

    1.  IEnumerable 与  IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...

  2. java笔记--笔试中极容易出错的表达式的陷阱

    我相信每一个学过java的人儿们都被java表达式虐过,各种"肯定是它,我不可能错!",然后各种"尼玛,真假,怎么可能?",虽然在实际开发中很少会真的让你去使用 ...

  3. 【Swift】iOS UICollectionView 计算 Cell 大小的陷阱

    前言 API 不熟悉导致的问题,想当然的去理解果然会出问题,这里记录一下 UICollectionView 使用问题. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http://www.cn ...

  4. JavaScript中的this陷阱的最全收集

    JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值.事实并非如此,在JavaScript中,最 ...

  5. 高性能MySQL(四):schema陷阱

    一.schema陷阱 二.缓存表和汇总表 三.范式和反范式

  6. JeeSite学习笔记~代码生成原理

    1.建立数据模型[单表,一对多表,树状结构表] 用ERMaster建立数据模型,并设定对应表,建立关联关系 2.系统获取对应表原理 1.怎样获取数据库的表 genTableForm.jsp: < ...

  7. C#_闭包陷阱

    如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中. 即将for循环中的变量i修改成了引用闭包对象的公共变量i.这样一来,即使代码执行后离开了原局部变量i的 ...

  8. 安装 Linux 时碰到的硬盘分区的陷阱及应对

    硬盘分区的陷阱及应对 之所以想到写这篇,是因为本人在折腾 Linux 系统的过程中,有多次掉入硬盘分区的陷阱的经历.最近几天,再一次掉入坑中,折腾了两天才从坑中爬出来.经过多方查询资料,终于弄明白了硬 ...

  9. NULL的陷阱:Merge

    NULL表示unknown,不确定值,所以任何值(包括null值)和NULL值比较都是不可知的,在on子句,where子句,Merge或case的when子句中,任何值和null比较的结果都是fals ...

随机推荐

  1. .NET--------未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项。

    未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项.找到的程序集清单定义与程序集引用不匹配. (异常来自 HRESULT:0x80131040)   解决方 ...

  2. 微信小程序 画布drawImage实现图片截取

    大多数图片都大小不一,选择框的尺寸也是宽高相等的,就会有图片被压缩 解决方法: 1.可以使用画布对图片先进行截取,保存截取图片(用户自己选取,或者指定图片中心区域截取),但是对于多张图片手动截取,会影 ...

  3. Lavarel Route::resource

    RESTful Resource controller A RESTful resource controller sets up some default routes for you and ev ...

  4. Linux 操作系统目录结构

    /  根目录 # ls /bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp  var bin - ...

  5. elasticsearch(4) 轻量搜索

    一 空搜索 搜索API的最基础的形式是没有指定任何查询的空搜索 ,它简单地返回集群中所有索引下的所有文档: 示例 GET 127.0.0.1:9200/_search 响应 { , "tim ...

  6. 函数QFileSystemModelPrivate::_q_fileSystemChanged

    由于Qt的体系过于庞大,即使是某个模块,分析起来也十分困难.对于QFileSystemModel,我们在这里对单个重要函数慢慢分析 1 /*! \internal The thread has rec ...

  7. elasticsearch的监控脚本

    监控elasticsearch cluster 集群 通过主动模式将数据发送给zabbix server import json import struct import socket import ...

  8. 记Weblogic部署BUG(websocket)

    将含有websocket的SSM项目部署在Weblogic上面,遇到websocket报错如下 java.lang.ClassCastException: org.springframework.se ...

  9. Qt坑点汇总

    1.场景:假如我们想在layout中的qlabel中设置一个图片 1.1 如果简单地使用border-image,我们可以做到,并且拖动界面时,label可以随布局正常变化,这里需要注意的是,修改ui ...

  10. 监测NGINX服务的shell脚本

    Nginx 虽然处理并发量比 apache 确实要强点,但它这种 php-cgi 模式不是太稳定,这点网上也有朋友总结了,我在实现项目中也感受到了. 偶尔会出现以下情况的:php-cgi 进程突然消失 ...