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());
所以,即使在
- if(!roleIdListOperator.containsAll(roleIdListOld)){
- addMessage(model, "修改使用者資訊失敗, 您的許可權不足");
- UserUtils.clearCache();
- return form(oldUser, model);
- }
- 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;
- }
因此特別需要注意快取的使用,不是任何地方都適合使用快取。
相關文章
- 雲端計算服務,多雲管理需要注意的陷阱!
- 避免陷阱,重現Equals方法您需要注意的其中2個原則
- jeesite檔案結構與jeesite.properties
- jeeSite分頁
- 開發中常遇到的Python陷阱和注意點Python
- Go 需要注意的坑Go
- 那些你需要注意的坑
- 需要注意的unix命令使用
- jeesite專案筆記筆記
- MySQL 中處理 Null 時要注意兩個陷阱MySqlNull
- LEFT JOIN 需要注意的點(Presto)REST
- 我們需要注意的 immutable 操作
- delete與delete[]需要注意的地方delete
- golang defer使用需要注意Golang
- 建設網站時需要注意的網站
- Javascript需要注意的幾個運算子JavaScript
- 架構那些需要注意的事兒架構
- golang split需要注意的一個點Golang
- oracle設定process需要注意的事情Oracle
- 租用伺服器時需要注意的伺服器
- 10個需要注意的SQL問題SQL
- margin-top使用需要注意的地方
- felx佈局,需要定寬的,需注意:
- /etc/fstab檔案需要注意的地方
- 配置Oracle RAC需要注意的問題Oracle
- 在GlassFish的WebService中需要注意的HashMapWebHashMap
- JeeSite Spring Cloud安裝搭建SpringCloud
- Go的50度灰:Golang新開發者要注意的陷阱和常見錯誤Golang
- 物流延遲填申請單?注意暗藏勒索軟體陷阱
- 使用HTTP需要注意什麼?HTTP
- springMVC的@ResponseBody、@RequestBody使用需要注意的地方SpringMVC
- oracle over()的使用和需要特別注意的地方Oracle
- Oracle remap_schema需要注意的問題OracleREM
- idc伺服器租用需要注意的伺服器
- 安裝rac時需要注意的問題
- 網站的設計需要注意什麼?網站
- Python初學者需要注意的問題Python
- vmware搭建HMC需要注意的幾個點