此文爲轉載:http://www.diybl.com/course/1_web/webjs/2007113/82989.html 我們回到正題上,除了logger,還有boardDAO(當然要了) ,userGroupDAO,boardPermissionDAO(講過的),permissionDAO,roleDAO,forumDAO,forumHistoryDAO,還有一些Cache服務:sysListObjCache,boardCache,userPermissionCache(以前用過),還有一個額外的服務類:sysStatService;對他們進行getter/setter方法一下... 我們分析下sysStatService:(applicationContext.xml) 系統統計服務 //每次請求都產生一個對象 userConfig指: ${bbscs.safePath} //安全目錄我的指的是D:/safe/ UserConfig是一個config配置服務,在服務類中首先注入了safePath 這個String對象,它向外提供了String getUserFilePath(String userID)[簡單,最終得到一個用戶文件Path,見實際目錄就可知道其原理]和String getIndexPath()和File getIndexFilePath()以及boolean indexExist()四個對外公共方法.詳細看方法實現: public String getIndexPath() { //好象沒用到過 StringBuffer sb = new StringBuffer(); sb.append(this.getSafePath()); sb.append("index/"); return sb.toString(); } public File getIndexFilePath() { File indexFilePath = new File(this.getIndexPath());//構造一個File對象 if (!indexFilePath.exists()) { indexFilePath.mkdirs(); } return indexFilePath; } public boolean indexExist() { File file = new File(this.getIndexPath() + "segments"); return file.exists();//使用文件是否存在的方法作判斷! } 可見config服務類較簡單(沒用到logger)!提供一個常用服務!而com.laoer.bbscs.service.imp.SysStatService是怎麼用到它的呢?它繼承了抽象類SysStatService,在這個抽象類中,有一個東西是私有的: private long onlineNum = 0; private long appearTime = 0; 發表時間long類型 private String appearTimeStr = ""; private long allUserNum = 0; private String lastRegUser = ""; private long postMainNum = 0; private long postNum = 0; (這個東西和safe文件夾下的sysstat.properties好像啊,看看先: #sysstat.properties #Mon Jul 16 11:35:16 CST 2007 //appearTimeStr ?? onlineNum=2 postMainNum=1 allUserNum=2 appearTime=963209825828 postNum=1 lastRegUser=sgwood )下面有其get/set方法,子類可以繼承哦!當然,還有一個子類需實現的方法: public abstract void load(); public abstract void saveOnline(long nowonlinenum);//在線數 public abstract void saveAllUserNum(long allusernum, String lastreguser);//所有用戶數和最後註冊用戶名 public abstract void savePostNum(long main, long all); //存入主題貼和總數 接着我們看下SysStatServiceImp.java文件好了:它加入了logger和UserConfig對象先,看下面的load方法: public void load() { Properties prop = new Properties(); File f = new File(this.getUserConfig().getSafePath() + "sysstat.properties"); //getSafePath()當然會暴露出來! if (f.exists()) { try { FileInputStream fis = new FileInputStream(f); prop.load(fis); /**只需傳遞這個文件的 InputStream 給 load() 方法,就會將每一個鍵-值對添加到 Properties 實例中。然後用 list() 列出所有屬性或者用 getProperty() 獲取單獨的屬性。 list() 方法的輸出中鍵-值對的順序與它們在輸入文件中的順序不一樣。 Properties 類在一個散列表(hashtable,事實上是一個 Hashtable 子類)中儲存一組鍵-值對,所以不能保證順序。 */ this.setOnlineNum(Long.parseLong(prop.getProperty("onlineNum", "0").trim()));//繼承過來的!!! this.setAppearTime(Long.parseLong(prop.getProperty("appearTime", "0").trim())); this.setAllUserNum(Long.parseLong(prop.getProperty("allUserNum", "0").trim())); this.setLastRegUser(prop.getProperty("lastRegUser", "")); this.setPostMainNum(Long.parseLong(prop.getProperty("postMainNum", "0").trim())); this.setPostNum(Long.parseLong(prop.getProperty("postNum", "0").trim())); this.setAppearTimeStr(Util.formatDateTime(new Date(this.getAppearTime())));//寫時間用 fis.close(); } catch (NumberFormatException ex) { logger.error(ex); } catch (FileNotFoundException ex) { logger.error(ex); } catch (IOException ex) { logger.error(ex); } } else { save(); //文件不存在時!內部private方法哦 } } private void save() { String path = this.getUserConfig().getSafePath() + "sysstat.properties"; Properties prop = new Properties(); prop.setProperty("onlineNum", String.valueOf(this.getOnlineNum())); prop.setProperty("appearTime", String.valueOf(this.getAppearTime())); prop.setProperty("allUserNum", String.valueOf(this.getAllUserNum())); prop.setProperty("lastRegUser", this.getLastRegUser()); prop.setProperty("postNum", String.valueOf(this.getPostNum())); prop.setProperty("postMainNum", String.valueOf(this.getPostMainNum())); try { FileOutputStream fos = new FileOutputStream(path);//寫入新文件中 prop.store(fos, "sysstat.properties"); fos.close(); } catch (FileNotFoundException ex) { logger.error(ex); } catch (IOException ex) { logger.error(ex); } } 我們看下對抽象類的實現吧:(先load一下資源文件,再set相關的key值,最後用私有的save一下) public void saveAllUserNum(long allusernum, String lastreguser) { this.load(); this.setAllUserNum(allusernum); this.setLastRegUser(lastreguser); this.save(); } public void saveOnline(long nowonlinenum) { this.load(); if (nowonlinenum > this.getOnlineNum()) { //好象不太對,不然只有多沒有少! long atime = System.currentTimeMillis(); this.setOnlineNum(nowonlinenum); this.setAppearTime(atime); this.setAppearTimeStr(Util.formatDateTime(new Date(atime))); this.save(); } } public void savePostNum(long main, long all) { this.load(); this.setPostMainNum(main); this.setPostNum(all); this.save(); } OK!終於可以回到BoardServiceCachImp實現類中了,繞了好大一個彎!由於還有許多東西沒用過,只能推測一下其用法了哦~~~還是先看幾個,createBoard(Board board)先: @SuppressWarnings("unchecked") public Board createBoard(Board board) throws BbscsException { try { Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父級版區 if (pboard != null) { // 父級版區存在 List pboards = new ArrayList(); pboards.addAll(pboard.getParentIDs());//父的祖先 pboards.add(pboard.getId());//父 /** addAll(Collection c) add(int index,Elelemt e) 注意parentIDs和childIDs均爲List是由自定義userType的 public Class returnedClass() { return List.class; } 決定返回是List類型的! */ board.setParentIDs(pboards); // 設置父級版區列表字段 board.setLevel(pboard.getLevel() + 1); // 設置級別,在父級別上+1 } board = this.getBoardDAO().saveBoard(board);//這裏的其它參數由添加時決定,不需要改變,或由其它因素相關!由DAO完成實質的工作 if (pboard != null) { List pcboards = this.getBoardDAO().findBoardsByParentID(board.getParentID(), 1, -1, Constant.FIND_BOARDS_BY_ORDER); /** 取得父級半區的所有子版區列表pcboard,1是useStat,-1是hidden,FIND_BOARD_BY_ORDER=0另外有, public static final int FIND_BOARDS_BY_MAINPOSTNUM = 1; public static final int FIND_BOARDS_BY_POSTNUM = 2; /* List cids = this.getBoardIDs(pcboards);//得到子版ID的List /** public List getBoardIDs(List boards) { List l = new ArrayList(); for (int i = 0; i < boards.size(); i++) { Board b = (Board) boards.get(i); l.add(b.getId()); } return l; } */ pboard.setChildIDs(cids); // 設置父級版區的所有子版區列表字段 this.getBoardDAO().saveBoard(pboard);//更新父信息 this.getBoardCache().remove(pboard.getId()); /**從Board Cache中清除, 我們看下BoardCache的bean定義: 仍然是這個,以前講過! ${cache.config} cache.config=oscache.properties public void remove(Object key) { logger.debug("Remove from cache [Key:" + key + "]"); this.admin.flushEntry(key.toString()); } 從oscache.properties配置處的Cache內容中去掉key=pboard.getID()的對象,讓OSCache自動緩存內容吧! */ } this.clearBoradListSysListCache(board.getParentID()); /**它其實是一私有方法哦!需要注意的是實質用了SysListOjbCache,它其中的對象標識竟是[][][][][],5555,可能有些對象後三者可不用: private void clearBoradListSysListCache(long pid) { String[] useStats = { "-1", "0", "1" }; String[] hiddens = { "-1", "0", "1" }; String[] orderTypes = { "0", "1", "2" }; for (int i = 0; i < useStats.length; i++) { for (int j = 0; j < hiddens.length; j++) { for (int x = 0; x < orderTypes.length; x++) { this.getSysListObjCache().remove( "[B][" + pid + "][" + useStats[i] + "][" + hiddens[j] + "][" + orderTypes[x] + "]"); } } } } */ // 爲版區增加用戶組版區權限 List gl = this.getUserGroupDAO().findUserGroupsAll(); /**取得用戶組列表 public List findUserGroupsAll() { return this.getHibernateTemplate().find(LOADS_ALL); } private static final String LOADS_ALL = "from UserGroup order by id"; 原始數據爲6個記錄的表(見數據庫),id1---6,以GroupName區分之,TypeID表示代表類型,默認是系統類型的,不能刪除,用戶自己建的就是另外一個類型了。 */ BoardPermission bp; for (int i = 0; i < gl.size(); i++) { UserGroup ug = (UserGroup) gl.get(i); bp = new BoardPermission(); bp.setBoardID(board.getId().longValue());//冗餘字段 bp.setGroupID(ug.getId().intValue());//冗餘字段 switch (ug.getId().intValue()) { case 1: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1); /** break; case 2: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_2); break; case 3: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_3); break; case 4: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_4); break; case 5: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_5); break; case 6: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_6); break; default: bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1); } this.getBoardPermissionDAO().saveBoardPermission(bp); /**而在Constant.java中有段static段: for (int i = 0; i < BOARD_PERMISSION_GROUP_1.length; i++) { BOARD_PERMISSION_GROUP_LIST_1.add(new Long(BOARD_PERMISSION_GROUP_1[i])); } for (int i = 0; i < BOARD_PERMISSION_GROUP_2.length; i++) { BOARD_PERMISSION_GROUP_LIST_2.add(new Long(BOARD_PERMISSION_GROUP_2[i])); } for (int i = 0; i < BOARD_PERMISSION_GROUP_3.length; i++) { BOARD_PERMISSION_GROUP_LIST_3.add(new Long(BOARD_PERMISSION_GROUP_3[i])); } for (int i = 0; i < BOARD_PERMISSION_GROUP_4.length; i++) { BOARD_PERMISSION_GROUP_LIST_4.add(new Long(BOARD_PERMISSION_GROUP_4[i])); } for (int i = 0; i < BOARD_PERMISSION_GROUP_5.length; i++) { BOARD_PERMISSION_GROUP_LIST_5.add(new Long(BOARD_PERMISSION_GROUP_5[i])); } */ } return board; } catch (Exception e) { logger.error(e); throw new BbscsException(e); } } 從創建中可以知道,它調用了兩個cache(sysListObjCache和boardCache,對父級和本級的相關項進行更新(或產生),再保存.還有就是對此分版區(論壇)的用戶權限的寫入.總的來說,原則上,要數據服務(保存和查找)從DAO層來,要Cache從服務層來,要常用服務也從服務層來(因爲它不參與DAO工作) 接下來,我們繼續看findBoardAllTree: @SuppressWarnings("unchecked")//類型安全問題 public List findBoardsAllTree(long pid, List topList, int useStat, int hidden, int orderType) { List l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden, orderType);//這個pid指的是當前論壇,與前面的pid不一樣哦 for (int i = 0; i < l.size(); i++) { Board b = (Board) l.get(i); topList.add(b); this.findBoardsAllTree(b.getId().longValue(), topList, useStat, hidden, orderType);//由於topList是一個引用類型,可遞歸得到All子論壇及子論壇的子論壇... } return topList; } 而這個服務類的findBoardByParentID(非DAO)它首先是查詢SysListObjCache裏面有沒有,若沒有的話再由DAO從數據庫中找,且放入到SysListObjCache中,用了add方法,需注意的是其命名有點怪哦! public List findBoardsByParentID(long pid, int useStat, int hidden, int orderType) { List l = (List) this.getSysListObjCache().get( "[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]"); if (l == null) { // l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden, // orderType); l = this.getBoardDAO().findBoardIdsByParentID(pid, useStat, hidden, orderType); this.getSysListObjCache().add("[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]", l); } List bl = new ArrayList(); if (l != null && !l.isEmpty()) { for (int i = 0; i < l.size(); i++) { // Board b = this.getBoardByID(((Long)l.get(i)).longValue()); Board b = this.getBoardByID((Long) l.get(i)); if (b != null) { bl.add(b); } } } 由於Cache中不一定是Borad對象了,由於add進的是list,便由List類型的bl重新加載一次爲Board的List.findBoardInIDs根據IDs一個取一個,邊放入到l中! public List findBoardsInIDs(List ids) { List l = new ArrayList(); if (ids != null && !ids.isEmpty()) { for (int i = 0; i < ids.size(); i++) { // Board b = this.getBoardByID(((Long) ids.get(i)).longValue()); Board b = this.getBoardByID((Long) ids.get(i)); if (b != null) { l.add(b); } } } return l; } return bl; } 與其相反的方法是: public List getBoardIDs(List boards) { List l = new ArrayList(); for (int i = 0; i < boards.size(); i++) { Board b = (Board) boards.get(i); l.add(b.getId()); } return l; } 注意以上都有this.getBoardByID(),它是帶Cache的哦!從BoardCache中取! public Board getBoardByID(long id) { Board board = (Board) this.getBoardCache().get(new Long(id)); if (board == null) { board = this.getBoardDAO().getBoardByID(id); if (board != null) { this.getBoardCache().add(board.getId(), board); } } return board; } 另外,對於Permission,有以下幾個方法: public Map[] getBoardMasterPermission(int roleID) public Map[] getBoardPermission(long bid, int groupID) private Map[] getPermissionMaps(long bid, int groupID) private Map[] getPermissionMaps(int roleID) 前面2個是公開的方法,後2個是被調用的.. public Map[] getBoardMasterPermission(int roleID) { if (Constant.USE_PERMISSION_CACHE) { Map[] mapPermission = (Map[]) this.getUserPermissionCache().get("R_" + String.valueOf(roleID)); if (mapPermission == null) { mapPermission = this.getPermissionMaps(roleID); this.getUserPermissionCache().add("R_" + String.valueOf(roleID), mapPermission); } return mapPermission; } else { return this.getPermissionMaps(roleID); } } private Map[] getPermissionMaps(int roleID) { Map[] mapPermission = { new HashMap(), new HashMap() }; //MAP數組,555 Role role = this.getRoleDAO().findRoleByID(roleID);//得到id爲roleID的角色對象 List permissions = role.getPermissions(); // 取得角色的權限ID列表(ID的List) if (permissions != null && !permissions.isEmpty()) { List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得權限列表Permission對象的List for (int i = 0; i < permissionList.size(); i++) { Permission permission = (Permission) permissionList.get(i); if (permission.getTypeID() == 2) { mapPermission[0].put(permission.getResource() + "," + permission.getAction(), permission); } if (permission.getTypeID() == 3) { mapPermission[1].put(permission.getId(), permission); } //這段需理解!!! } } return mapPermission; } 另外,除了版主外,還有版區權限: public Map[] getBoardPermission(long bid, int groupID) { if (Constant.USE_PERMISSION_CACHE) { Map[] mapPermission = (Map[]) this.getUserPermissionCache().get( "BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID)); if (mapPermission == null) { mapPermission = this.getPermissionMaps(bid, groupID); this.getUserPermissionCache().add("BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID), mapPermission); } return mapPermission; } else { return this.getPermissionMaps(bid, groupID); } } 同樣,它用了類似的重載方法: private Map[] getPermissionMaps(long bid, int groupID) { Map[] boardPermission = { new HashMap(), new HashMap() }; BoardPermission bp = this.getBoardPermissionDAO().findBoardPermissionByBidGid(bid, groupID);//用的是BoardPermissionDAO List permissions = bp.getPermissions(); // 取得權限ID列表 if (permissions != null && !permissions.isEmpty()) { List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得權限列表Permission對象的List for (int i = 0; i < permissionList.size(); i++) { Permission permission = (Permission) permissionList.get(i); if (permission.getTypeID() == 2) { boardPermission[0].put(permission.getResource() + "," + permission.getAction(), permission); } if (permission.getTypeID() == 3) { boardPermission[1].put(permission.getId(), permission); } } } return boardPermission; } 接下來,看看getNextOrder(long pid),getPostSumNum(int mainorall,int usStat,int hidden),它們完全由DAO去查詢數據庫!我們看下remove系列: public void removeBoard(Board board) throws BbscsException { try { Long lbid = board.getId(); long pbid = board.getParentID(); Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父版區 this.getBoardDAO().removeBoard(board); // 刪除版區 this.getBoardPermissionDAO().removeBoardPermissionsByBid(board.getId().longValue());//刪除對應版區的權限 if (pboard != null) { // 父版區存在,對ChildIDs字段做矯正 List pcboards = this.getBoardDAO().findBoardsByParentID(pboard.getId().longValue(), 1, 0, Constant.FIND_BOARDS_BY_ORDER);//pcboards是子對象 List cids = this.getBoardIDs(pcboards);//cids是子IDs pboard.setChildIDs(cids); this.getBoardDAO().saveBoard(pboard); } this.getBoardCache().remove(lbid); this.clearBoradListSysListCache(pbid); //清理本id的BoardCache,清理父id的SysListObjectCache,而userPermission中出現過去2類:R_ BG_的..都不好清理! } catch (Exception ex) { logger.error(ex); throw new BbscsException(ex); } } public void removeBoardTag(Board board, String tagID) throws BbscsException { BoardTag bt = null; Iterator it = board.getBoardTag().iterator(); //由Iterator遍歷查找,找到後remove掉 while (it.hasNext()) { bt = (BoardTag) it.next(); if (bt.getId().equals(tagID)) { board.getBoardTag().remove(bt); break; } } try { board = this.getBoardDAO().saveBoard(board); //保存修改結果 this.getForumDAO().updateForumsTag(tagID, "0", "");//帖子TAG this.getForumHistoryDAO().updateForumsTag(tagID, "0", "");//歷史貼TAG this.getBoardCache().remove(board.getId());//BoardCache清理一次 } catch (Exception e) { logger.error(e); throw new BbscsException(e); } } 最後再看兩個方法: public Board updateBoard(Board board, long oldParentID) throws BbscsException { try { Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); if (pboard != null) { List pboards = new ArrayList(); pboards.addAll(pboard.getParentIDs()); pboards.add(pboard.getId()); board.setParentIDs(pboards); board.setLevel(pboard.getLevel() + 1); } else { board.setParentIDs(new ArrayList());//hbm.xml決定需List對象 board.setLevel(0); } /** 數據庫中的數據id /parentID/ParentIDs/ChildIDs 1/ 0/ /2,3 2/ 1/ 1/ 3/ 1/ 1/ */ board = this.getBoardDAO().saveBoard(board); if (pboard != null) { List pcboards = this.getBoardDAO().findBoardsByParentID(board.getParentID(), 1, -1, Constant.FIND_BOARDS_BY_ORDER); List cids = this.getBoardIDs(pcboards); pboard.setChildIDs(cids); this.getBoardDAO().saveBoard(pboard); this.getBoardCache().remove(pboard.getId()); } this.clearBoradListSysListCache(board.getParentID()); if (oldParentID != -1) { // 父級版區改變。修正父級版區數據 關鍵點,由傳入的oldParentID決定是否改變ParentID Board pboardOld = this.getBoardDAO().getBoardByID(oldParentID); if (pboardOld != null) { List pcboards = this.getBoardDAO().findBoardsByParentID(pboardOld.getId().longValue(), 1, -1, Constant.FIND_BOARDS_BY_ORDER); List cids = this.getBoardIDs(pcboards); pboardOld.setChildIDs(cids); this.getBoardDAO().saveBoard(pboardOld); this.getBoardCache().remove(pboardOld.getId()); this.clearBoradListSysListCache(oldParentID); } } this.getBoardCache().remove(board.getId()); // 從Cache中清除 return board; } catch (Exception ex) { logger.error(ex); throw new BbscsException(ex); } } 最後一個方法了: public void saveBoardsPostNumCount() throws BbscsException { long totalNum = 0; long totalMainNum = 0; List bl = findBoardsByParentID(0, 1, -1, Constant.FIND_BOARDS_BY_ORDER);//從0繼承的版區有顯示主題數和貼子數的需求 for (int i = 0; i < bl.size(); i++) { Board b = (Board) bl.get(i); if (b.getBoardType() == 3) { //可發貼的版區吧! b.setMainPostNum(this.getForumDAO().getForumNum(b.getId(), 1, 0, 0, -1) + this.getForumHistoryDAO().getForumNum(b.getId(), 1, 0, 0, -1)); b.setPostNum(this.getForumDAO().getForumNum(b.getId(), -1, 0, 0, -1) + this.getForumHistoryDAO().getForumNum(b.getId(), -1, 0, 0, -1)); //isNew=-1表示非主題貼,而1則相反! try { b = this.getBoardDAO().saveBoard(b); totalNum = totalNum + b.getPostNum(); totalMainNum = totalMainNum + b.getMainPostNum(); // if (Constant.USE_CLUSTER) { this.getBoardCache().remove(b.getId()); // 從Cache中清除 // } else { // this.getBoardCache().add(b.getId(), b); // } } catch (Exception ex1) { logger.error(ex1); throw new BbscsException(ex1); } } List bl2 = findBoardsByParentID(b.getId(), 1, -1, Constant.FIND_BOARDS_BY_ORDER); //子論壇的更新 if (!bl2.isEmpty()) { for (int j = 0; j < bl2.size(); j++) { Board b2 = (Board) bl2.get(j); if (b2.getBoardType() == 3) { b2.setMainPostNum(this.getForumDAO().getForumNum(b2.getId(), 1, 0, 0, -1) + this.getForumHistoryDAO().getForumNum(b2.getId(), 1, 0, 0, -1)); b2.setPostNum(this.getForumDAO().getForumNum(b2.getId(), -1, 0, 0, -1) + this.getForumHistoryDAO().getForumNum(b2.getId(), -1, 0, 0, -1)); try { b2 = this.getBoardDAO().saveBoard(b2); totalNum = totalNum + b2.getPostNum(); totalMainNum = totalMainNum + b2.getMainPostNum(); // if (Constant.USE_CLUSTER) { this.getBoardCache().remove(b2.getId()); // 從Cache中清除 // } else { // this.getBoardCache().add(b2.getId(), b2); // } } catch (Exception ex1) { logger.error(ex1); throw new BbscsException(ex1); } } } } } logger.info("postMainNum:" + totalMainNum + " postNum:" + totalNum);//打印 this.getSysStatService().savePostNum(totalMainNum, totalNum);//SysStatService服務終於用上了! }
對天乙社區bbscs8實現的詳細分析二
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.