JeeSite缓存的坑

   这里对于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;  
    }

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值