此文为转载: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实现的详细分析二
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.