OMG,到底在尋找什么..................
          (構(gòu)造一個完美的J2EE系統(tǒng)所需要的完整知識體系)
          posts - 198,  comments - 37,  trackbacks - 0

          Struts-menu源碼分析

          ????????好的代碼讀起來讓人如飲醍醐,讀完以后神清氣爽。如果你想提高你的編程水平,如果你想提高你的設計能力,如果你也想成為大師,那么就去閱讀代碼吧。以本人十幾年來的編程經(jīng)驗,閱讀代碼能讓你得到的比閱讀文章(那怕是大師的文章)得到的更多。優(yōu)秀而且實用的代碼有很多,比如Junit,比如Jive,比如petStore,甚至是tomcat的Example、Log4j的Example。

          ????????一段廣告完畢,下面就為大家分析一下struts-menu的源碼,作為送給大家的圣誕禮物吧。Struts-Menu也來自一位大師的作品, Matt Raible。有很多優(yōu)秀的作品,比如使用struts和hibernate的struts-resume。官方網(wǎng)站是http://raibledesigns.com/wiki/Wiki.jsp?page=Main。Struts-Menu的最新版本是2.1。功能是使用struts技術(shù),構(gòu)建樹形菜單。應該說是一個非常實用的技術(shù),極大的方便了廣大的開發(fā)人員。與此同時,個人認為它的作用還不止于些。比如,同時它也是一個使用Mavenvelocity的一個很好的例子。

          ????????首先,我們?nèi)タ匆幌滤男Ч?a target="_blank">http://www.raibledesigns.com/struts-menu/。可以看到,如此豐富多彩的菜單效果,都是在演示一個配置文件里的內(nèi)容。這是一個非常好的數(shù)據(jù)與表示相分離的實現(xiàn)。我們打開它的源碼來看。首先看一下它的包圖

          共有五個包,其中menu自然是完成數(shù)據(jù)組織功能,是核心之一,displayer是顯示方式包,完成數(shù)據(jù)顯示大部分功能。也是核心之一。taglib意義明顯。example自然是一些example。util是讀取資源文件的包。因些,我們重點研究的包只有三個menu,displayer和taglib。

          首先我們來看menu包的類圖

          首先是MenuPlugIn這個類。這個類的功能很明顯,就是一個struts的plug-in。可以看到,它只有一個參數(shù)menuConfig,就是menu的配置文件路徑。果然,在struts-conf文件中有這么一段

            <!-- ========== Plug Ins Configuration ================================== -->

          <plug-in className="net.sf.navigator.menu.MenuPlugIn">
          <set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/>
          </plug-in>

          ?

          說明配置文件來自于/WEB-INF/menu-config.xml,當然,我們可以找到相應路徑下找到這個文件。如果你以前沒有做過struts的plug-in,現(xiàn)在該知道怎么做了吧,就這么簡單。通過閱讀初始化函數(shù),知道它的功能就是調(diào)用MenuRepository來建立菜單。因此。我們知道MenuRepository必然是一個組織管理管理菜單的組織類。

          public void init(ActionServlet servlet, ModuleConfig config)
          		throws ServletException {
          		if (log.isDebugEnabled()) {
          			log.debug("Starting struts-menu initialization");
          		}
          
          		this.servlet = servlet;
          		repository = new MenuRepository();
          		repository.setLoadParam(menuConfig);
          		repository.setServlet(servlet);
          
          		try {
          			repository.load();
          			servlet.getServletContext().setAttribute(
          				MenuRepository.MENU_REPOSITORY_KEY,
          				repository);
          
          			if (log.isDebugEnabled()) {
          				log.debug("struts-menu initialization successfull");
          			}
          		} catch (LoadableResourceException lre) {
          			throw new ServletException(
          				"Failure initializing struts-menu: " + lre.getMessage());
          		}
          	}

          打開MenuRepository類,我們可以看到這個類也很簡單,不過已經(jīng)有少可以學習的了。首先是FastHashMap,可以看到,這個類里有三個FastHashMap。顧名思議,是快速HashMap了,再看一下,它來自org.apache.commons.collections.FastHashMap;。看到org.apache.commons這個著名的包了?如果你以前從沒使用過它,那么建議你花上一段時間去研究使用它,我保證物有所值。

          protected FastHashMap menus = new FastHashMap();
          protected FastHashMap displayers = new FastHashMap();
          protected FastHashMap templates = new FastHashMap();

          接下來我們看到log的定義。對了,log,調(diào)試的核心之一。而下面這一句則是commons log的最常用的使用方法。快快讓你的程序使用上commons log吧,第一,它功能強大,第二,它使用簡單,就是這么簡單。

          private Log log = LogFactory.getLog(getClass().getName());

          下面看一個的函數(shù)

           protected Digester initDigester() {
                  Digester digester = new Digester();
                  digester.setClassLoader(Thread.currentThread().getContextClassLoader());
                  digester.push(this);
          
                  //digester.setDebug(getDebug());
                  // 1
                  digester.addObjectCreate("MenuConfig/Menus/Menu",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu");
                  digester.addSetNext("MenuConfig/Menus/Menu", "addMenu");
          
                  // 2
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent",
                      "net.sf.navigator.menu.MenuComponent");
          
                  // 3        
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item/Item",
                      "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
          
                  // 4
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item",
                      "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
          
                  // 5
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
          
                  // 6
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
          
                  // 7
                  digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "net.sf.navigator.menu.MenuComponent", "type");
                  digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
                  digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
                      "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
          
                  digester.addObjectCreate("MenuConfig/Displayers/Displayer",
                      "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping");
                  digester.addSetProperties("MenuConfig/Displayers/Displayer");
                  digester.addSetNext("MenuConfig/Displayers/Displayer",
                      "addMenuDisplayerMapping",
                      "net.sf.navigator.displayer.MenuDisplayerMapping");
                  digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty",
                      "property", "value");
                      
                  return digester;
              }

          這里又是一個經(jīng)典,digester,Digester的使用,如果你需要讀一個XML配置文件,并且不想與DOM直接打交道的話,Digester將是一個很好的選擇。實際上我們看到load函數(shù)調(diào)用一句 digester.parse(input);就已經(jīng)把menu-config.xml建立到內(nèi)存里了,就這么簡單。如果你想要初始化你的系統(tǒng),這種方法是不是可以學習呢?"工欲善其事,必先利其器"。我們可以看到Raible是怎么樣利用現(xiàn)有的工具來減輕開發(fā)量的。

          由于MenuRepository舉重若輕的初始化過程,甚至都沒有讓我們看到樹形結(jié)構(gòu)是怎么建立到內(nèi)存里去的。不過不要著急,類圖給我們了明示。

          看到MenuBase類了嗎?對了,看名字就知道是一個Menu的基類。可以看到,它是一個簡單的JavaBean。而且相信它的每個屬性大家根據(jù)名字也能猜出來。所以重點講解是MenuComponent,一個簡化的 "Composite"模式。

          如上圖所示。由于此處的Leaf沒有任何方法,只有屬性。因此Leaf和Composite收縮成了一個MenuComponent類。大家都知道,Composite模式是實現(xiàn)樹形結(jié)構(gòu)最好的方法。如果你以前沒有機會實現(xiàn)或者沒有從Composite模式得到好處,那么,從這里看一下用Composite模式得到的好處。首先看它的簡單,MenuComponet的實際代碼很少,加起來不到十行。

          public void addMenuComponent(MenuComponent menuComponent) {
                  menuComponents.add(menuComponent);
                  menuComponent.setParent(this);
          
                  if ((menuComponent.getName() == null) ||
                          (menuComponent.getName().equals(""))) {
                      menuComponent.setName(this.name + menuComponents.size());
                  }
              }
          
              public MenuComponent[] getMenuComponents() {
                  MenuComponent[] menus =
                      (MenuComponent[]) menuComponents.toArray(_menuComponent);
          
                  return menus;
              }

          如果你用十行來實現(xiàn)一個樹型結(jié)構(gòu)(并且還是通用的),你愿不愿意?就是通過簡單的這么一些代碼,實現(xiàn)的在內(nèi)存中建立樹型結(jié)構(gòu)的目標。

          下面我們來看DispLay包,這個包的功能也是很清楚的,就是用來顯示啦。這個包的類圖非常漂亮,遺憾的是也非常大。只能縮小了給大家看了。

          從類圖中可以看到一個非常極漂亮的面象對象的設計思路。通過一個接口,利用模板方法。最后具體實現(xiàn)樹型結(jié)構(gòu)的顯示。其主要方法是displayComponents和display這兩方法,init方法則實現(xiàn)了初始化的工作,讀取javascript和圖片等文件。displayComponents是一個迭代函數(shù)。從而可以遍歷一個MenuCompont樹。并將其顯示出來。

          應該說,Menu包是一個M層,而Dispplya包是一個view層,而加上TagLib包,就實現(xiàn)了MVC的完整結(jié)構(gòu)。

          兩個Tag類很清楚,首先我們從怎么使用它來看它們實現(xiàn)的功能

          <menu:useMenuDisplayer name="ListMenu"
          bundle="org.apache.struts.action.MESSAGE">
          ????????<menu:displayMenu name="ToDoListMenuFile"/>
          ????????<menu:displayMenu name="ToDoListMenuEdit"/>
          ????????<menu:displayMenu name="CaseDetailMenuCase"/>
          ????????<menu:displayMenu name="Standalone"/>
          </menu:useMenuDisplayer>

          顯而易見。useMenuDisplayer這個類是實現(xiàn)使用哪一種顯示方式。在menu-config里我們看到ListMenu的定義

          <Displayer name="ListMenu"
          type="net.sf.navigator.displayer.ListMenuDisplayer"/>

          displayMenu則是取得一菜單,并將其顯示出來,同樣在menu-config也能找到。

          <Menu  name="ToDoListMenuEdit"  title="EDIT">
          <Item name="TDLselect" title="SELECT_ALL" image="images/select-all.png"
          location="index.jsp" width="100" />
          <Item name="TDLprefs" title="USER_PREFERENCES" image="images/prefs.png"
          location="index.jsp" width="150" />
          <Item title="Action Test" action="setPermissions?displayer=${displayer}"/>
          </Menu>

          查看 DisplayMenu的代碼,可以看到。它完成的功能只是從context里取得MenuComponent對象,然后通過 displayer.display(menu);把它交給一個MenuDisplayer的實例來負責畫出來。

          因此,Control層很好的完成了控制的功能。

          綜上所述。通過這樣一個優(yōu)美的設計,把各個功能都他離開來了。如果我們需要增加一種顯示方式,只要繼承MenuDisplayer或它的一個子類,然后寫出我們的方法,而不需要修改系統(tǒng)的其他部分。同樣的,如果我們的菜單不準備存放在ServletContext而準備存放在比如Session里了,那么我們也只需要修改control部分和生成部分(即MenuRepository)部分。而不影響Display部分。

          OK,對struts-menu的介紹結(jié)束了,下一篇文章將是如果使用struts-menu和數(shù)據(jù)庫技術(shù)動態(tài)生成菜單了。請大家繼續(xù)關(guān)注我的網(wǎng)站。

          ?

          紫龍,于 12/22/2003 16:45:09

          原貼地址:http://champion.ewuxi.com/old/opensource/struts-new/strutsmenu.htm????????????????? 藍色天空版權(quán)所有

          posted on 2006-10-26 20:18 OMG 閱讀(504) 評論(0)  編輯  收藏 所屬分類: Struts

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           

          <2006年10月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          IT風云人物

          文檔

          朋友

          相冊

          經(jīng)典網(wǎng)站

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 华安县| 黔西县| 莱西市| 格尔木市| 偏关县| 湘乡市| 赞皇县| 南漳县| 金门县| 泸定县| 色达县| 布拖县| 宾川县| 鹤庆县| 西昌市| 耒阳市| 丹东市| 土默特左旗| 湖南省| 阿拉善左旗| 临颍县| 常宁市| 怀集县| 齐齐哈尔市| 赞皇县| 习水县| 龙门县| 松江区| 北碚区| 永嘉县| 广州市| 石柱| 伊春市| 淅川县| 景泰县| 商丘市| 长沙市| 柳江县| 开江县| 新竹县| 上蔡县|