黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

Struts-menu源碼分析

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

?????? 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)建樹形菜單。應(yīng)該說是一個(gè)非常實(shí)用的技術(shù),極大的方便了廣大的開發(fā)人員。與此同時(shí),個(gè)人認(rèn)為它的作用還不止于些。比如,同時(shí)它也是一個(gè)使用 Maven velocity 的一個(gè)很好的例子。

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

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

首先我們來看menu包的類圖

首先是MenuPlugIn這個(gè)類。這個(gè)類的功能很明顯,就是一個(gè)struts的plug-in。可以看到,它只有一個(gè)參數(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,當(dāng)然,我們可以找到相應(yīng)路徑下找到這個(gè)文件。如果你以前沒有做過struts的plug-in,現(xiàn)在該知道怎么做了吧,就這么簡單。通過閱讀初始化函數(shù),知道它的功能就是調(diào)用 MenuRepository來建立菜單。因此。我們知道MenuRepository必然是一個(gè)組織管理管理菜單的組織類。

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

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

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

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

下面看一個(gè)的函數(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;     }
            

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

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

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

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

              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;     }
            

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

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

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

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

兩個(gè)Tag類很清楚,首先我們從怎么使用它來看它們實(shí)現(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這個(gè)類是實(shí)現(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對(duì)象,然后通過 displayer.display(menu);把它交給一個(gè)MenuDisplayer的實(shí)例來負(fù)責(zé)畫出來。

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

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

OK,對(duì)struts-menu的介紹結(jié)束了,下一篇文章將是如果使用struts-menu和數(shù)據(jù)庫技術(shù)動(dòng)態(tài)生成菜單了。

?

Struts-menu源碼分析


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論