关于jeesite的陷阱需要注意

xiaoxiao2021-02-28  110

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

例如如下的接口

[java] view plain copy print ? @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";      }   @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:

[java] view plain copy print ? 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;  }   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; }

这里的

[java] view plain copy print ? systemService.getUser(user.getId());   systemService.getUser(user.getId());会一直拿到该对象实例的缓存值,而该值,在修改提交到服务端时,框架已经更新了,再进到controller中。

所以,即使在

[java] view plain copy print ? if(!roleIdListOperator.containsAll(roleIdListOld)){              addMessage(model, "修改用户信息失败, 您的权限不足");              UserUtils.clearCache();              return form(oldUser, model);          }   if(!roleIdListOperator.containsAll(roleIdListOld)){ addMessage(model, "修改用户信息失败, 您的权限不足"); UserUtils.clearCache(); return form(oldUser, model); }这里返回了,其他地方获取该user的值 [java] view plain copy print ? getUser(user.getId());   getUser(user.getId());还是会是缓存的值。

也相当于单例的、全局的实例值

解决方法:

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

[java] view plain copy print ? @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";      }   @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:

[java] view plain copy print ? /**  * 根据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;  }   /** * 根据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; }

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

转载请注明原文地址: https://www.6miu.com/read-17403.html

最新回复(0)