zoukankan      html  css  js  c++  java
  • 关于jeesite的陷阱需要注意

    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里获取,导致,后来获取的对象的数据都是错误的(修改但保存失败的),这就变相单例化,而且是无法获得正确数据了。

    例如如下的接口

     1 @RequiresPermissions("sys:user:edit")
     2     @RequestMapping(value = "save")
     3     public String save(User user, HttpServletRequest request, Model model, RedirectAttributes redirectAttributes) {
     4 
     5         
     6         //判断是否有权限修改用户信息
     7         
     8         //先清缓存:因为框架原因,只要更新了该用户,则会同步更新该用户缓存,从而无法获得真正的该用户信息,所以需要清除掉该缓存,这里先注释掉,看问题
     9         //UserUtils.clearCache(user);
    10         User oldUser = systemService.getUser(user.getId());
    11         List<String>roleIdListOld = oldUser.getRoleIdList();
    12         User operator = UserUtils.getUser();
    13         List<String>roleIdListOperator = operator.getRoleIdList();
    14         //自己不能修改自己的权限
    15 //        if(user.getId().equals(operator.getId())){
    16 //            addMessage(model, "修改用户信息失败, 不能修改自己的权限");
    17 //            UserUtils.clearCache();
    18 //            return form(oldUser, model);
    19 //          }
    20         if(!roleIdListOperator.containsAll(roleIdListOld)){
    21             addMessage(model, "修改用户信息失败, 您的权限不足");
    22             UserUtils.clearCache();
    23             return form(oldUser, model);
    24         }
    25         user.setRoleList(roleList);
    26         // 保存用户信息
    27         systemService.saveUser(user);
    28         // 清除当前用户缓存
    29         if (user.getPhone().equals(UserUtils.getUser().getPhone())){
    30             UserUtils.clearCache();
    31             //UserUtils.getCacheMap().clear();
    32         }
    33         addMessage(redirectAttributes, "保存用户'" + user.getPhone() + "'成功");
    34         return "redirect:" + adminPath + "/sys/user/list?repage";
    35     }

    再看下getUser:

     1 public static User getUser(String id){
     2         User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_ID_ + id);
     3         if (user ==  null){
     4             user = userDao.get(id);
     5             if (user == null){
     6                 return null;
     7             }
     8             user.setRoleList(roleDao.findList(new Role(user)));
     9             CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
    10             CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getPhone(), user);
    11         }
    12         return user;
    13     }

    这里的

    1 systemService.getUser(user.getId());

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

    所以,即使在

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

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

    1 getUser(user.getId());

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

    解决方法:

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

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

     这里的getUserFromDB:

     1 /**
     2      * 根据ID获取用户——通过DB
     3      * @param id
     4      * @return 取不到返回null
     5      */
     6     public static User getUserFromDB(String id){
     7     
     8         User user = userDao.get(id);
     9         user.setRoleList(roleDao.findList(new Role(user)));
    10         return user;
    11     }

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

  • 相关阅读:
    Linux系统下安装rz/sz命令及使用说明
    Linux 下Beanstalk安装
    Jetty中间件
    JBOSS应用中间件
    IBM 存储高可用HA解决方案和DR连续性解决方案
    Nginx负载均衡与反向代理的配置和优化
    NFS挂载网络存储
    使用集中式身份管理服务详解
    配置链路聚合(端口聚合)
    配置ssh远程访问策略
  • 原文地址:https://www.cnblogs.com/wjlwo2ni/p/10588799.html
Copyright © 2011-2022 走看看