<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-67  評論-522  文章-0  trackbacks-0
        通過上一篇文章(dom4j實戰(一)——使用dom4jXML中讀取數據源配置),使我們對dom4j有了一些初步的認識和了解,也掌握了基本的操作方法,本文將承接前篇,借鑒Openfire項目中菜單設計的優點,結合我們自己的需求,通過一個小實例,來說明一下如何使用dom4j來實現這個功能。
        可以在http://www.igniterealtime.org/downloads/index.jsp 這里下載到Openfire的發布版和源碼版,Openfire的介紹在這里不作描述,網上相關的資料有很多,有興趣的朋友可以下載源碼研究一下,其實不了解Openfire也沒關系,因為我們只是借鑒其中的一些思想,等做完了實例,就會有一個比較直觀的認識。
        大象建議先在最后面下載源碼和必要的JAR包,讓程序運行起來看下效果,再看下面的詳細說明。
        開發環境:Eclipse 3.2.1  MyEclipse 5.10GA  Tomcat 6.10  
                 dom4j-1.6.1.jar  jaxen-1.1-beta-7.jar  sitemesh-2.2.1.jar

        1、創建tag-console.xml
           在src目錄下建一個tag-console.xml文件,這個配置文件中寫的是菜單信息,內容如下:
    <?xml version="1.0" encoding="GBK"?>
    <bookstore>
        
    <global>
            
    <appname>菜單導航demo</appname>
            
    <version>ver 1.0</version>
            
    <creator>菠蘿大象</creator>
        
    </global>
        
    <catalog id="catalog-program" name="編程開發" url="index.jsp" description="編程開發">    
            
    <item id="item-program-java" name="Java開發" url="index.jsp" description="Java開發">
                
    <book id="695043" name="Struts2 深入詳解" url="index.jsp" description="Struts2 深入詳解"/>
                
    <book id="691254" name="Ant整合開發" url="book_ant.jsp" description="Ant整合開發"/>
                
    <book id="693668" name="Java編程思想" url="book_java.jsp" description="Java編程思想"/>
            
    </item>
            
    <item id="item-program-database" name="數據庫開發" url="book_oracle9.jsp" description="數據庫開發">
                
    <book id="691245" name="Oracle 9i數據庫寶典" url="book_oracle9.jsp" description="Oracle 9i數據庫寶典"/>
                
    <book id="693254" name="SQL Server 2005應用開發" url="book_sqlserver.jsp" description="SQL Server 2005應用開發"/>
                
    <book id="690215" name="Oracle 10g高級開發" url="book_oracle10.jsp" description="Oracle 10g高級開發"/>
            
    </item>
        
    </catalog>
        
    <catalog id="catalog-system" name="系統相關" url="book_vista.jsp" description="系統相關">
            
    <item id="item-system-windows" name="Windows系統" url="book_vista.jsp" description="Windows系統">
                
    <book id="691258" name="Windows Vista入門" url="book_vista.jsp" description="Windows Vista入門"/>
                
    <book id="695489" name="Windows注冊表實戰" url="book_windows.jsp" description="Windows注冊表實戰"/>
            
    </item>
            
    <item id="item-system-linux" name="Linux系統" url="book_linux9.jsp" description="Linux系統">
                
    <book id="696598" name="Linux 9.0詳解" url="book_linux9.jsp" description="Linux 9.0詳解"/>
                
    <book id="694585" name="Linux寶典" url="book_linux.jsp" description="Linux寶典"/>
            
    </item>
        
    </catalog>
    </bookstore>
           上面XML里面的東西我是隨便寫的,大家千萬不要較真,我用圖書來做菜單一是方便大家理解,另一個是簡化程序,其實Openfire的服務器端是一個后臺管理系統,它是基于XMPP(可擴展消息處理現場協議)開發的,XMPP貫穿整個系統設計,如果你想用它的控制臺框架,但又不想用XMPP,請先從網頁入口開始,結合頁面仔細分析代碼,把需要的部分抽取出來就行了,其它的不用去管。大象沒有研究過XMPP,只是抽取了控制臺框架,對Openfire的源代碼也沒能深入的研究,最主要還是E文太爛了。^_^
           Openfire沒有采用現在很流行的技術架構(SSH),只使用JSP+JavaBean,但是它有自己的系統設計,就連日志都是自己做的,沒有使用我們熟悉的log4j,真的是太佩服鳥~~~~
        2、創建ResourceManage.java
           在util包下創建ResourceManage類,這個類主要是用來讀取tag-console.xml文件,并取得文件中的基本信息,以及查找元素等操作。
           我們先在Constant接口中,增加一個字符串常量:String TAG_CONFIG = "tag-console.xml"
           ResourceManage前面加載資源的部分和上一篇,后來修改過的DataBaseConnect類一樣,只需把Constant.DB_CONFIG換成Constant.TAG_CONFIG就行了。接下來,在類中加入幾個讀取XML中基本信息的方法:
        <global>
            
    <appname>菜單導航demo</appname>
            
    <version>ver 1.0</version>
            
    <creator>菠蘿大象</creator>
        
    </global>
           這里只舉出取得appname元素值的方法,其它的幾個都很相似,請查看源代碼。
        /**
         * 得到應用程序名稱
         
    */
        
    public static String getAppName(){
            Element appName 
    = (Element) coreModel.selectSingleNode("//bookstore/global/appname");
            
    if(appName!=null){
                
    return appName.getText();
            }
    else{
                
    return null;
            }
        }
           根據id屬性值查找對應的元素:
        /**
         * 在整個文檔節點中查找id屬性值為傳入id的元素對象
         * 
    @param id 待查找的id屬性值
         * 
    @return 返回找到的元素對象
         
    */
        
    public static Element getSingleElementById(String id){
            
    return (Element)coreModel.selectSingleNode("//*[@id='"+id+"']");
        }
           這里用到了XPATH語法,根據傳入的id值,在整個文檔中查找id屬性值與此一致的元素對象。用下面的代碼舉例說明:
        <book id="695043" name="Struts2 深入詳解" url="index.jsp" description="Struts2 深入詳解"/>
           當傳入的id屬性值為"695043"時,那么我們就會得到對應這個id值的book元素對象,id屬性值在整個配置文件中就是一個key關鍵字,起到定位的作用。
           根據id屬性值查找上下文中對應的catalog元素
        /**
         * 根據傳入的id查找上下文中對應的catalog元素
         * 
    @param id 待查找的id屬性值
         * 
    @return 返回id值所在的catalog元素對象
         
    */
        
    public static Element getElementByID(String id) {
            
    return (Element) coreModel.selectSingleNode("http://*[@id='" + id
                    + "']/ancestor::catalog");

        }
           ancestorXPATH語法中軸的概念,我引用網上官方文檔中的說明:“ancestor(axis)包含上下節點的祖先節點,該祖先節點由其上下文節點的父節點以及父節點的父節點等等諸如此類的節點構成,所ancestor軸總是包含有根節點,除非上下文節點就是根節點本身。這句話的意思其實就是向上查找節點,直到找到根節點為止。對于ancestor::catalog來說,就是向上查找直到catalog節點為止。所以getElementByID這個方法是根據傳入的id屬性值在上下文中查找節點,直到找到這個id值所在的上下文catalog節點為止。當傳入的id屬性值為"695043"時,我們會得到id="catalog-program"這個catalog節點元素,而不會得到id="catalog-system"這個catalog節點元素。這樣說大家大概能明白是什么意思了吧?
           可以去這個網站看下XPATH教程:http://www.zvon.org/xxl/XPathTutorial/General_chi/examples.html
        3、自定義標簽  
           采用自定義標簽的方式來生成菜單,借助ResourceManage類取出XML文件中的信息,將這些內容裝載到標簽體中,然后在JSP頁面中呈現出來。
           1、主菜單標簽
              主菜單有兩個,catalog元素這一層表示主菜單。標簽的實現類如下:
              MainTag.java
              建com.demo.tag包,在tag包下創建MainTag類,繼承javax.servlet.jsp.tagext.BodyTagSupport類,主要的部分代碼如下,完整代碼請下載源碼包查看。
              這些屬性與demo.tld中的屬性對應,每個屬性都有settergetter方法。
        private String css; //菜單的CSS樣式
        private String currentcss; //當前選中菜單的CSS樣式

             doStartTag()方法是遇到標簽開始時調用的方法,EVAL_BODY_BUFFERED表示創建一個緩沖流,將標簽體的內容保存到BodyContent對象中,可以對其內容進行修改。BodyContent繼承了javax.servlet.jsp.JspWriter類,BodyContent對象的內容不自動寫入servlet的輸出流,而是放在一個字符流緩存中。當標簽體完成后其對象仍可在doEndTag()方法中應用,由getString()getReader()方法操作。并在必要時修改及寫入恢復的JspWriter輸出流。EVAL_BODY_INCLUDE表示將顯示標簽間的文字。另一個返回值是SKIP_BODY,它表示不顯示標簽間的文字。

        public int doStartTag() throws JspException {
            
    return EVAL_BODY_BUFFERED; //創建保存到BodyContent對象中的緩沖流
        }
              doEndTag()方法是遇到標簽結束時調用的方法,EVAL_PAGE表示處理完標簽后繼續執行標簽之后的JSP頁面。另一個返回值SKIP_PAGE表示不處理標簽之后的JSP網頁。
        public int doEndTag() throws JspException {
            
    //代碼主體省略,請查看源碼
            return EVAL_PAGE; //處理完標簽后繼續執行標簽之后的JSP頁面
        }
              doEndTag()方法中部分比較重要的代碼說明:
              使用pageContext對象在JSP頁面上下文取得請求,不過請注意pageContext,它定義在javax.servlet.jsp.tagext.TagSupport中,而不是在BodyTagSupport中,因為BodyTagSupport繼承了TagSupport
        //使用pageContext對象在JSP頁面上下文取得請求
        HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
              取得請求中的pageID值,這個pageID值在每個jsp頁面中放在meta標簽中,通過sitemesh裝飾器取出放到request中。
        /*
         * 從請求中得到pageID值,即每個JSP里meta的content值
         * 與XML文件中book元素的id屬性值一致
         
    */
        String pageID 
    = (String)request.getAttribute("pageID");
              將所有的catalog元素取出放到List集合中,這里是只取得catalog這一層級的元素,實質就是catalogs中只有兩個對象,一個是id="catalog-program",另一個是id="catalog-system",使用dom4j我們會發現處理元素非常容易,API相當的豐富,想寫成什么樣完全憑你自己的想法。
        //將所有的catalog元素取出放到List集合
        List catalogs = ResourceManage.getCoreModel().selectNodes("//catalog");
              看看前面介紹的getElementByID這個方法,這個currentCatalog所表示就是pageID所在的catalog元素。上面的代碼是為了和下面的代碼結合來判斷當前的菜單是否為選中,加入CSS樣式顯示。
        //pageID所在的catalog元素,主要用來判斷當前菜單是否被選中
        Element currentCatalog = (Element)ResourceManage.getElementByID(pageID);
              從BodyContent中將標簽體緩存流讀取出來,標簽在WebRoot/decorators/main.jsp<a href="[url]" title="[description]" onmouseover="self.status='[description]';return true;" onmouseout="self.status='';return true;">[name]</a>
        String value = getBodyContent().getString(); //得到標簽體
              Catalogs里面是兩個catalog對象,循環遍歷取出,將標簽體中的[id][url]、[name]、[description]替換為XML文件中的屬性值,這樣主菜單標簽就生成了。
        for (int i=0; i<catalogs.size(); i++) {
            Element catalog 
    = (Element)catalogs.get(i); //catalog元素對象
            String value = getBodyContent().getString(); //得到標簽體
            /*
             * 將標簽體中的[id]、[url]、[name]、[description]
             * 替換為XML文件中的屬性值
             * attributeValue方法是取屬性值
             
    */
            
    if (value != null) {
                value 
    = StringUtils.replace(value, "[url]", request
                           .getContextPath()
                           
    + "/" + catalog.attributeValue("url"));            
                value 
    = StringUtils.replace(value, "[name]",catalog.attributeValue("name"));
                value 
    = StringUtils.replace(value, "[description]",catalog.
                        attributeValue(
    "description"));
            }
            String css 
    = getCss();
            
    //對當前選中菜單添加CSS樣式
            if (catalog.equals(currentCatalog)) {
                css 
    = getCurrentcss();
            }
            buf.append(
    "<li class=\"").append(css).append("\">");
            
    if (i > 0) {
                buf.append(
    " | ");
            }
            buf.append(value).append(
    "</li>");
        }
           2、導航菜單及側邊欄菜單標簽
               導航菜單每個catalog下都有兩個,而側邊欄菜單則在item下定義,這兩個標簽類與主菜單的標簽類沒有太大的區別,主要就是生成標簽體,匹配CSS樣式,因此,代碼中相同的部分我不再細述,只說一下不同的地方。
               在tag包下創建NavTag類和SideTag類,標簽屬性與MainTag一樣,只是SideTag多了一個headercss屬性,這是在頁面顯示時,加在邊欄上當前選中項左側小箭頭的CSS樣式,不清楚的話,請運行程序后觀察。
               NavTag.java
               根據pageID找到此元素對象:
        //根據pageID找到此元素對象,即book元素對象
        Element current = ResourceManage.getSingleElementById(pageID);
               如果current不為空,取得父節點,其為item元素。根據pageID值,如果為695043,則subnavid="item-program-java"item元素,如果為691245,則subnavid="item-program-database"item元素。這個subnav的作用也是用來判斷當前的菜單是否為選中,加入CSS樣式顯示。
        Element subnav = null;
        
    if (current != null) {
            subnav 
    = current.getParent(); //取得父節點,即item元素
        }
               SideTag.java
               在SideTag中也有上面的代碼,但是subnav不再與CSS有關,而是取得它的所有子元素集合,即book元素集合,然后遍歷所有book節點,取出屬性值放入標簽體中再輸出到頁面。
               我注釋寫得很詳細,請查看代碼了解細節。
        4、創建StringUtils.java
           在util包下創建StringUtils類,這個類作為字符串處理類。添加public static String replace(String string, String oldString, String newString)方法,它的作用就是將標簽體中的[id]、[url]、[name]、[description]替換為XML文件中的屬性值。如果被替換的字符串在標簽體中有多個,也能將它全部替換。
        /**
         * 將string中的oldString全部替換為newString
         * 
    @param string 原始字符串
         * 
    @param oldString 被替換的字符串
         * 
    @param newString 要替換的字符串
         * 
    @return 返回替換完后的新string
         
    */
        
    public static String replace(String string, String oldString, String newString) {
            
    if (string == null) {
                
    return null;
            }
            
    int i = 0;
            
    //判斷string中是否有被替換的字符串,i其實是索引值
            if ((i = string.indexOf(oldString, i)) >= 0) {
                
    char[] string2 = string.toCharArray(); //字符串放入數組
                char[] newString2 = newString.toCharArray(); //要替換的字符串
                int oLength = oldString.length(); //被替換的字符串的長度
                StringBuilder buf = new StringBuilder(string2.length);
                
    /*
                 * 從索引0開始,按i值的長度在string2數組中截取字符
                 * 將截取的字符放到buf中,接著再加入要替換的內容
                 
    */
                buf.append(string2, 
    0, i).append(newString2);
                i 
    += oLength; //得到被替換字符結束位置的索引
                int j = i;
                
    /*
                 * 查找string中,是否仍然含有被替換字符串
                 * 使用循環,將所有oldString換成newString
                 
    */
                
    while ((i = string.indexOf(oldString, i)) > 0) {
                    buf.append(string2, j, i 
    - j).append(newString2);
                    i 
    += oLength; //得到被替換字符結束位置的索引
                    j = i;
                }
                
    /*
                 * 截取string2數組中從索引j開始
                 * string2.length-j的長度加到buf中
                 * 其實就是在buf中補全標簽體
                 
    */
                buf.append(string2, j, string2.length 
    - j);
                
    return buf.toString();
            }
            
    return string;
        }
           如果看注釋就能懂那最好不過,如果不明白,在這上面打個斷點調試一下,就會十分清楚了。這個方法是Openfire中的源代碼,不過全是E文,大象先也看不明白,后來調試了一下,知道是怎么回事了,特加上注釋和大家一起分享這個好東東。
           寫完了自定義標簽類,我們還需要自定義標簽文件,在WEB-INF目錄下新建demo.tld,代碼不帖出來了,使用源碼中的就行。
        5、裝飾器
           后臺的Java類,我們全部寫完了,現在開始完成前臺部分,在頁面顯示上,Openfire使用了sitemesh裝飾器框架,它能幫助我們在由大量頁面構成的項目中創建一致的頁面布局和外觀,如一致的導航條,一致的banner,一致的版權等等。至于怎樣使用sitemesh這里不作介紹了,請自行去搜索相關資料,這部分內容網上很多的,sitemesh比較簡單,很容易上手。
           使用裝飾器,需要導入JAR包,在本例中,大象使用的是sitemesh-2.2.1.jar包,將jar包加到WEB-INF/lib目錄中,然后修改web.xml,添加如下代碼:
        <filter>  
            
    <filter-name>sitemesh</filter-name>  
            
    <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>  
        
    </filter >  
        
    <filter-mapping>  
            
    <filter-name>sitemesh</filter-name>  
            
    <url-pattern>/*</url-pattern>  
        
    </filter-mapping>
           然后在WEB-INF下新建decorators.xml文件,內容如下:
        <decorators defaultdir="/decorators">
            
    <decorator name="main" page="main.jsp">
                
    <pattern>/index.jsp</pattern>
                
    <pattern>/book_*.jsp</pattern>
            
    </decorator>
        
    </decorators>
           請注意defaultdir后面的值,這是你放裝飾器頁面的目錄位置。本例中,在WebRoot目錄下新建decorators文件夾,再在里面新建main.jsp,這個就是裝飾器頁面了。<pattern></pattern>之間的內容就是需要被裝飾的頁面,*號是通配符,可以代替任何字符。/book_*.jsp表示:使用main.jsp裝飾WebRoot目錄下所有以book_開頭的頁面,這里定義的name="main",可以在裝飾器頁面中使用,因為不一定只有一個裝飾器頁面,可能會有很多個。因此,在裝飾器頁面中為了布局效果會聯合使用多個裝飾器來修飾頁面,以達到簡化布局、降低維護難度、提高工作效率的作用。另外在使用時,請注意被裝飾頁面與裝飾器頁面之間的相對位置。
        6、main.jsp
            在頁面中引用被裝飾頁面的page對象:<decorator:usePage id="decoratedPage" />
            使用decoratedPage取得被裝飾頁面中meta標簽的content值,再將它放到request請求中,這樣在自定義標簽類中我們使用pageContext對象得到的請求就是這個。
        <%
            request.setAttribute(
    "pageID", decoratedPage.getProperty("meta.pageID"));
        %>
            顯示被裝飾頁面<title></title>之間的標題:<decorator:title />
            顯示被裝飾頁面body中的內容,被裝飾頁面的主體都將在這里顯示:<decorator:body/>
            除此之外,在main.jsp中,我們還發現大量的使用div來放置元素,并且每個標簽中都有id屬性,沒有看到任何的CSS樣式,其實是通過id屬性在demo.css文件進行了定義,所有的布局和顯示效果都在這個文件中進行了定義,這樣就達到了內容呈現與樣式布局相分離的結果,方便以后的修改和維護,這種做法用的人現在已經越來越多,大家趕快行動吧!
           sitemesh中還有一個sitemesh.xml文件,如果程序中沒有特別需求,可以不用加入它,我們也能在sitemesh-2.2.1.jar中找到,com.opensymphony.module.sitemesh.factory目錄下有一個sitemesh-default.xml文件,這就是sitemesh默認的配置文件。
        7、顯示頁面
           在tag-console.xmlurl屬性里定義了顯示頁面,接下來我們把這些頁面都做好,內容很簡單,本文只是演示Openfire的菜單設計思想,用容易懂的例子來說明,以便大家能夠快速了解。
          index.jsp頁面的代碼:
    <%@ page contentType="text/html; charset=utf-8" %>
    <html>
    <head>
        
    <title>Struts2 深入詳解</title>
        
    <meta name="pageID" content="695043"/>
    </head>
    <body>
        
    <center><h1>Struts2 深入詳解</h1></center>
    </body>
    </html>
           其余的幾個頁面內容大致一樣,把content值、<title></title>標題、以及<center><h1></h1></center>之間的內容換成book元素中定義的屬性值即可。
           demo.css
           在WebRoot目錄下新建css文件夾,再在里面創建demo.css文件。我直接把Openfire的樣式表COPY過來。然后把里面沒用的部分刪除了,體積小了不少。
           圖片
           我從Openfire中只取了本例用到的圖片,如果是專業美工,完全可以設計出自己的菜單風格。
        8、發布項目
           我們在web.xml中可以加入下面一段代碼,index.jsp作為我們的默認顯示頁面:
        <welcome-file-list>
            
    <welcome-file>index.jsp</welcome-file>
        
    </welcome-file-list>
           把demo部署到%TOMCAT_HOME%\webapps目錄下,啟動tomcat,在地址欄中輸入:http://localhost:8080/demo 看看效果是不是和下面的一樣:

           大家覺得這個菜單的顯示方式怎么樣呢?偶覺得這用來做后臺管理到還是不錯滴,如果是其它的信息管理系統,那這個配置文件的內容就會很恐怖了,其實還可以把XML中的中文信息保存到國際化資源文件中,這樣可以實現多語種版本以及簡化維護。各位有什么好的意見或建議,可以和我留言或E-mail給我。大象也想把自己的一點心得拿出來和大家分享。
           點擊下載:demo源碼
           點擊下載:dom4j-1.6.1.jar  jaxen-1.1-beta-7.jar  sitemesh-2.2.1.jar
           本文為菠蘿大象原創,如要轉載請注明出處
    posted on 2008-08-17 23:35 菠蘿大象 閱讀(3977) 評論(0)  編輯  收藏 所屬分類: dom4j&jdom
    主站蜘蛛池模板: 亚洲国产成人久久一区久久| 日韩插啊免费视频在线观看| 亚洲avav天堂av在线网毛片| 亚洲jjzzjjzz在线观看| 亚洲精品中文字幕无码AV| 久久久久亚洲AV片无码下载蜜桃| 亚洲国产精品国自产拍AV| 亚洲性在线看高清h片| 亚洲一区二区三区免费| 在线日韩日本国产亚洲| 国内精品99亚洲免费高清| 亚洲自偷自偷在线制服| 亚洲一区二区女搞男| 亚洲成a人片77777kkkk| 亚洲国产精品自在在线观看 | 久久精品视频免费看| 无码人妻一区二区三区免费看| 久久成人免费播放网站| 9420免费高清在线视频| 最好看的中文字幕2019免费| 91麻豆最新在线人成免费观看 | 十八禁的黄污污免费网站| 国产亚洲视频在线| eeuss影院免费直达入口| 99久久成人国产精品免费| 日韩视频免费在线观看| 成人免费大片免费观看网站| 四虎成人免费大片在线| 免费又黄又爽又猛的毛片| 永久亚洲成a人片777777| 亚洲午夜未满十八勿入| 激情亚洲一区国产精品| 色噜噜的亚洲男人的天堂| eeuss影院www天堂免费| 最近2019中文字幕免费大全5| 成年午夜视频免费观看视频| 亚洲AV无码乱码在线观看性色扶| 精品国产亚洲一区二区三区| 亚洲午夜精品一区二区公牛电影院 | 拔擦拔擦8x华人免费久久 | 香蕉97超级碰碰碰免费公|