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. blender 3d模型软件介绍(开源,免费,易用,强大)

    关于BLENDER Blender是一个开源的多平台轻量级全能三维动画制作软件 具有建模,雕刻,绑定,粒子,动力学,动画,交互,材质,渲染,音频处理,视频剪辑以及运动跟踪,后期合成等等的一系列动画短片 ...

  2. Excel修改证件照图片背景色

    在实际生活中,我们常常需要把白底证件照更改为蓝底或红底.操作步骤只需4步!   工具/原料   Excel 方法/步骤     第1步:插入白底证件照 单击[插入]---[图片],选择图片保存的位置, ...

  3. Python机器学习(基础篇---监督学习(朴素贝叶斯))

    朴素贝叶斯 朴素贝叶斯分类器的构造基础是贝叶斯理论.采用概率模型来表述,定义x=<x1,x2,...,xn>为某一n维特征向量,y∈{c1,c2,...ck}为该特征向量x所有k种可能的类 ...

  4. electron-vue初始桌面应用

    1.安装vue-cli脚手架 npm install -g vue-cli 2.创建项目:vue init simulatedgreg/electron-vue my-project 3.安装依赖 : ...

  5. OpenGL绘制一个四边形

    学习自:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/ OpenGL没有直接绘制四边形的a ...

  6. 使用 <embed> 标签显示 flash文件(swf)格式 ,如何设置 width 和 height 宽度,高度.

    1. embed 标签 支持  .swf 格式.     .flv 的不支持. 2. 通常情况下, 网站中上传 多个 flash文件. 它的默认大小是不一样的. 而且 可以 宽度 大于 高度(横向的) ...

  7. wpf binging Class 双向绑定 需要实现的接口

    public class HuMan : INotifyPropertyChanged { private string name; private string age; private strin ...

  8. 删除csdn上面自己上传的资源

    今天想删掉以前的资源,才发现CSDN并没有提供删除资源的功能,然后去网上搜了下,这才删除了,不知道怎么删除的小伙伴看过来~ 首先,找到自己要删除资源的页面,举个栗子 https://download. ...

  9. 初涉FlaskWeb开发----基础篇

    1.web程序运行的基本流程 {客户端发送请求 <-----> 服务器返回响应} 2.使用框架可以降低开发难度,提高开发效率. 3.Flask框架的基本认识: 特点:用Python语言实现 ...

  10. nginx和resin一二三

    最近听我们这边的开发讲解了一小下nginx和resin,现在Mark一下,可能不专业,但是对于我来说通俗易懂就好哈哈哈. 首先说服务器,服务器这个东西吧它是干啥的呢,你写的页面只能在你自己的电脑上打开 ...