jier的博客

          2007年9月30日 #

          從request獲取各種路徑總結(jié)

          request.getRealPath("url");//虛擬目錄映射為實(shí)際目錄
          request.getRealPath("./");//網(wǎng)頁所在的目錄
          request.getRealPath("../");//網(wǎng)頁所在目錄的上一層目錄

          request.getContextPath();//應(yīng)用的web目錄的名稱
             如http://localhost:7001/bookStore/  
              /bookStore/      =>  [contextPath]  (request.getContextPath())

          獲取Web項(xiàng)目的全路徑
          String strDirPath = request.getSession().getServletContext().getRealPath("/");



          posted @ 2007-10-12 10:30 夏雪 閱讀(2753) | 評論 (2)編輯 收藏

          XDoclet

          XDoclet起步

          XDoclet是一個代碼生成工具,它可以把你從Java開發(fā)過程中繁重的重復(fù)勞動中解脫出來。XDoclet可以讓你的應(yīng)用系統(tǒng)開發(fā)的更加快速,而你只要付比原先更少的努力。你可以把你手頭上的冗長而又必需的代碼交給它幫你完成,你可以逃脫“deployment descriptor地獄”,你還可以使你的應(yīng)用系統(tǒng)更加易于管理。而你所要做的,只不過是在你的注釋里,多加一些類javadoc屬性。然后,你會驚訝于XDoclet為了做到的一切。
          討論XDoclet,有一點(diǎn)比較容易產(chǎn)生混淆,那就是XDoclet不但是一系統(tǒng)的代碼生成應(yīng)用程序,而且它本身還是一個代碼生成框架。雖然每個應(yīng)用系統(tǒng)的細(xì)節(jié)千變?nèi)f化(比如EJB代碼生成和Struts代碼生成是不一樣的,而JMX代碼生成又是另一番景象),但這些代碼生成的核心概念和用法卻是類似的。
          在這一章里,我們將會看到滲透到所有XDoclet代碼生成程序當(dāng)中的XDoclet框架基礎(chǔ)概念。但在之前,我們先從一個例子入手。

          2.1 XDoclet in action

          每一個程序員都會認(rèn)識到,他們的程序永遠(yuǎn)也不會完成??倳辛硪恍┑墓δ苄枰砑樱硪恍┑腂UG需要修正,或者需要不斷的進(jìn)行重構(gòu)。所以,在代碼里添加注釋,提醒自己(或者其他的程序員)有哪些任務(wù)需要完成已成為一個共識。
          如何來跟蹤這些任務(wù)是否完成了呢?理想情況下,你會收集整理出來一個TODO任務(wù)列表。在這方面,XDoclet提供了一個強(qiáng)大的TODO生成器,來幫助你完成這個任務(wù)。這是一個把XDoclet引入項(xiàng)目的好機(jī)會。
          2.1.1 一個公共的任務(wù)
          假設(shè)你正在開發(fā)一個使用了勺子的類。
          public class Matrix {
            // TODO ? 需要處理當(dāng)沒有勺子的情況
            public void reload() {
              // ...
              Spoon spoon = getSpoon();
              // ...
            }
          }
          理想情況下,你在下一次閱讀這段代碼的時候,你會處理這個“空勺子”(null spoon)的問題。但如果你過了很久才回來看這段代碼,你還會記得在這個類里還有一些工作要做嗎?當(dāng)然,你可以在你的源碼里全局搜索TODO,甚至你的集成開發(fā)環(huán)境有一個內(nèi)建的TODO列表支持。但如果你想把任務(wù)所在的類和方法也標(biāo)注出來的話,XDoclet可以是另一種選擇。XDoclet可以為你的項(xiàng)目生成一個TODO報表。

          2.1.2 添加XDoclet標(biāo)簽
          為了把你的TODO項(xiàng)目轉(zhuǎn)換成另一種更加正式的格式,你需要對代碼進(jìn)行一些細(xì)微的改動。如下所示:
          public class Matrix {
            /** @todo 需要處理當(dāng)沒有勺子的情況 */
            public void reload() {
              // ...
            }
          }
          這里加入了一個XDoclet需要的類javadoc標(biāo)簽。XDoclet會使用這些標(biāo)簽標(biāo)記的信息,以及在這種情況下標(biāo)簽所處的類和方法,來生成TODO報表。

          2.1.3 與Ant集成
          要生成TODO報表,你需要確保在你的機(jī)器上正確安裝了XDoclet。
          在Ant任務(wù)里,最少要包含一個目標(biāo)(例如init目標(biāo))定義<documentdoclet>任務(wù),這是一個Ant自定義任務(wù),例如:
            <taskdef name=”documentdoclet”
              classname=”xdoclet.modules.doc.DocumentDocletTask”
              classname=”xdoclet.lib.path” />
          這個<documentdoclet>任務(wù)是XDoclet核心代碼生成應(yīng)用程序中的一個。
          現(xiàn)在,你可以在Ant構(gòu)建文件中加入一個todo目標(biāo)調(diào)用這個任務(wù)來生成TODO報表,如:
            <target name=”todo” depends=”init”>
              <documentdoclet destdir=”todo”>
                <fileset dir=”${dir.src}”>
                  <include name=”**/*.java” />
                </fileset>
                <info/>
              </documentdoclet>
            </target>
          <info>子任務(wù)會遍歷你的源文件,查找todo標(biāo)簽,并在todo子目錄下生成HTML格式的TODO報表。

          2.1.4 創(chuàng)建一個更加職業(yè)化的TODO報表
          XDoclet生成的TODO報表可以有一個更加職業(yè)化的外表。報表會列出一個概覽,顯示在哪個包哪個類里有todo項(xiàng)(以及todo項(xiàng)的個數(shù))。Todo項(xiàng)可以跟在方法、類和域上,從報表上可以清楚的區(qū)別它們。類級別的todo項(xiàng)會標(biāo)注class,方法級別的todo項(xiàng)會在方法簽名上標(biāo)注M。構(gòu)造函數(shù)和域相關(guān)的todo項(xiàng)也會進(jìn)行相似的標(biāo)注。
          這個任務(wù)看起來很簡單,但考慮到你所需要做的只是在注釋上添加一些格式化的@todo標(biāo)簽,相對于那種只有人才可以理解的無格式的松散的注釋,這種標(biāo)簽是機(jī)器可讀的,也更容易編程處理。生成的輸出也更容易閱讀并且更加的商業(yè)化。

          2.2 任務(wù)和子任務(wù)
          生成todo報表,只是XDoclet可以完成的事情當(dāng)中的冰山一角。當(dāng)初,XDoclet因?yàn)榭梢宰詣由蒃JB繁雜的接口和布署描述文件而聲名鵲起。然而,現(xiàn)在的XDoclet已經(jīng)發(fā)展成了一個全功能的、面向?qū)傩缘拇a生成框架。J2EE代碼生成只是XDoclet的一個應(yīng)用方面,它可以完成的任務(wù)已經(jīng)遠(yuǎn)遠(yuǎn)超越了J2EE和項(xiàng)目文檔的生成。

          2.2.1 XDoclet 任務(wù)
          到現(xiàn)在為止,我們一直在討論使用XDoclet生成代碼,但事實(shí)上,更確切的說法應(yīng)該是,我們使用XDoclet的一個特定的任務(wù)來生成代碼,比如<ejbdoclet>。每一個XDoclet任務(wù)關(guān)注于一個特定的領(lǐng)域,并提供這個領(lǐng)域的豐富的代碼生成工具。
          [定義:任務(wù)(Tasks)是XDoclet里可用的代碼生成應(yīng)用程序的高層概念。]
          在XDoclet里,目前已有如下所示的七個核心任務(wù)。
          <ejbdoclet>:面向EJB領(lǐng)域,生成EJB、工具類和布署描述符。
          <webdoclet>:面向Web開發(fā),生成serlvet、自定義標(biāo)簽庫和web框架文件。
          <hibernatedoclet>:Hibernate持續(xù),配置文件、Mbeans
          <jdodoclet>:JDO,元數(shù)據(jù),vender configuration
          <jmxdoclet>:JMX,MBean接口,mlets,配置文件。
          <doclet>:使用用戶自定義模板來生成代碼。
          <documentdoclet>:生成項(xiàng)目文件(例如todo列報表)
          這其中,<ejbdoclet>最常用,并且很多項(xiàng)目也僅僅使用XDoclet來進(jìn)行EJB代碼生成。<webdoclet>是其次一個常用的代碼生成任務(wù)。當(dāng)然,在一個項(xiàng)目中同時使用幾個XDoclet任務(wù)是可能的(并且也是推薦的),但在這些任務(wù)之間是完全獨(dú)立的,它們彼此之間并不能進(jìn)行直接的交流。

          2.2.2 XDoclet子任務(wù)
          XDoclet的任務(wù)是領(lǐng)域相關(guān)的,而在某個特定領(lǐng)域的XDoclet任務(wù),又由許許多多緊密耦合在一起的子任務(wù)組成的,這些子任務(wù)每個都僅僅執(zhí)行一個非常特定和簡單的代碼生成任務(wù)。
          [定義:子任務(wù)(subtasks)是由任務(wù)提供的單目標(biāo)的代碼生成過程]
          任務(wù)提供子任務(wù)執(zhí)行時的上下文,并且把這些相關(guān)的子任務(wù)組織管理了起來。任務(wù)會依賴這些子任務(wù)來生成代碼。在一個任務(wù)當(dāng)中調(diào)用多個子任務(wù)來協(xié)同完成各種各樣比較大型的代碼生成任務(wù)是非常常見的。比如,在開發(fā)EJB時,你可能想要為每一個bean生成一個home接口,一個remote接口以及ejb-jar.xml布署描述符文件。這就是在<ejbdoclet>任務(wù)的上下文環(huán)境中的三個獨(dú)立的代碼生成子任務(wù)。
          子任務(wù)可以隨意的組合排列,以滿足項(xiàng)目代碼生成的需要。某個XDoclet任務(wù)包含的子任務(wù)經(jīng)常會共享功能和在源文件中使用相同的XDoclet標(biāo)簽。這意味著當(dāng)你開始一個任務(wù)的時候,你可以很容易的集成進(jìn)一個相關(guān)的子任務(wù),而不需要很大的改動。

          子任務(wù)交互
          讓我們以<ejbdoclet>任務(wù)為例,看一下相關(guān)的子任務(wù)之間是如何進(jìn)行關(guān)聯(lián)的。假設(shè)你正在開發(fā)一個CMP(容器管理持久化)實(shí)體Bean。你想要使用一些<ejbdoclet>的子任務(wù):
          •<deploymentdescriptor>:生成ejb-jar.xml布署描述符文件。
          •<localhomeinterface>:生成local home接口。
          •<localinterface>:生成local接口。
          在執(zhí)行如上子任務(wù)的時候,你需要標(biāo)記出你的實(shí)體Bean的CMP域。當(dāng)你發(fā)布你的bean的時候,你還需要在開發(fā)商相關(guān)的布署描述符中提供某個特定的關(guān)系數(shù)據(jù)庫中的特定表和列與你的CMP實(shí)體Bean的映射關(guān)系。XDoclet可以讓你在原先已存在的CMP XDoclet屬性基礎(chǔ)上再加上一些關(guān)系映射屬性,然后,你就可以在任務(wù)中加入一個開發(fā)商相關(guān)的子任務(wù)(例如<jboss>或者<weblogic>)來生成布署描述符文件。XDoclet提供了幾乎所有的應(yīng)用服務(wù)器的支持,你只需要一些初始化的小改動,就可以進(jìn)行這些應(yīng)用服務(wù)器相關(guān)的代碼生成了。
          但那只是冰山一角。你還可以使用<entitycmp>子任務(wù)為為你的bean生成一個實(shí)體bean接口的實(shí)現(xiàn)子類。如果你使用<valueobject>子任務(wù)來為了你的bean生成值對象,<entityemp>子任務(wù)還會為你的值對象生成方法的實(shí)現(xiàn)代碼。
          覺得不可思議了吧。可惜XDoclet沒有提供<cupofcoffee>子任務(wù),要不然我們可以喝杯咖啡,休息一下啦。
          這里不是想向你介紹<ejbdoclet>所有的子任務(wù)或者<ejbdoclet>可以完成的所有代碼生成功能,而僅僅是想向你展示一下任務(wù)的子任務(wù)之間是如何工作在一起的。一旦你開始并熟悉了一個XDoclet 子任務(wù),熟悉另一個子任務(wù)會變得非常簡單- 那種每個子任務(wù)都是孤立的相比,使用這種可以相互協(xié)作的子任務(wù),開發(fā)成本會顯著的降低,效果也更加的立竿見影。 

          2.3 使用Ant執(zhí)行任務(wù)
          XDoclet“嫁”給了Ant。XDoclet任務(wù)就是Ant的自定義任務(wù),除此以外,沒有其他運(yùn)行XDoclet任務(wù)的方法。所幸的是,Ant已經(jīng)成為了Java構(gòu)建工具事實(shí)上的標(biāo)準(zhǔn),所以這不算什么限制。事實(shí)上,反過來,XDoclet與Ant的這種“親密”關(guān)系使得XDoclet可以參與到任何Ant構(gòu)建過程當(dāng)中去。
          2.3.1 聲明任務(wù)
          XDoclet并沒有和Ant一起發(fā)布,所以如果你想要使用XDoclet的話,就需要單獨(dú)的下載和安裝。在使用任何一個XDoclet的任務(wù)之前,你首先需要在使用Ant的<taskdef>任務(wù)來聲明它。例如:
          <taskdef name=”ejbdoclet”
            classname=”xdoclet.modules.ejb.EjbDocletTask”
            classpathref=”xdoclet.lib.path”/>
          如果你熟悉Ant的話,你就會知道這段代碼是告訴Ant加載<ejbdoclet>的任務(wù)定義。當(dāng)然,你也可以以你喜歡的任何方式來命名這個自定義任務(wù),但最好還是遵守標(biāo)準(zhǔn)的命名規(guī)律以免發(fā)生混淆。classname和classpathref屬性告訴Ant到哪里去找實(shí)現(xiàn)這個自定義任務(wù)的XDoclet類。如果你想使用其他的XDoclet任務(wù),就必須要類似這樣首先聲明這個任務(wù)。
          一般共通的做法是,把所有需要使用的XDoclet任務(wù)都放在Ant的一個目標(biāo)里聲明,這樣在其他的目標(biāo)里如果需要使用這些任務(wù),只要depends這個任務(wù)就可以了。你可能已經(jīng)在Ant的構(gòu)建文件里包含了init目標(biāo),這就是放置XDoclet任務(wù)聲明的好地方(當(dāng)然如果你沒有,你也可以建一個)。下面的例子就是在一個init目標(biāo)里加入了<ejbdoclet>和<webdoclet>的聲明:
          <target name=”init”>
            <taskdef name=”documentdoclet”
              classname=”xdoclet.modules.doc.DocumentDocletTask”
              classpathref=”xdoclet.lib.path” />
              <taskdef name=”ejbdoclet”
                classname=”xdoclet.modules.ejb.EjbDocletTask”
                classpathref=”xdoclet.lib.path” />
              <taskdef name=”webdoclet”
                classname=”xdoclet.modules.web.WebDocletTask”
                classpathref=”xdoclet.lib.path” />
          </target>
          現(xiàn)在,任務(wù)聲明好了,XDoclet“整裝待發(fā)”。

          2.3.2 使用任務(wù)
          你可以在任何目標(biāo)里使用聲明好的任務(wù)。在任務(wù)的上下文環(huán)境里,可以調(diào)動相關(guān)的子任務(wù)。讓我們看一個例子,這個例子調(diào)用了<ejbdoclet>任務(wù)。不要擔(dān)心看不懂語法的細(xì)節(jié),現(xiàn)在你只需要關(guān)心一些基礎(chǔ)概念就可以了。
          <target name=”generateEjb” depends=”init”>
            <ejbdoclet destdir=”${gen.src.dir}”>
              <fileset dir=”${src.dir}”>
                <include name=”**/*Bean.java”/>
              </fileset>
            <deploymentdescriptor destdir=”${ejb.deployment.dir}”/>
            <homeinterface/>
            <remoteinterface/>
            <localinterface/>
             <localhomeinterface/>
            </ejbdoclet>
          </target>
          把任務(wù)想像成一個子程序運(yùn)行時需要的一個配置環(huán)境(記住,子任務(wù)才是真正進(jìn)行代碼生成工作的)。當(dāng)調(diào)用一個子任務(wù)時,子任務(wù)從任務(wù)繼承上下文環(huán)境,當(dāng)然,你也可以根據(jù)需要隨意的覆蓋這些值。在上面的例子里,因?yàn)?lt;deploymentdescriptor>子任務(wù)生成的布署描述符文件和其他生成各種接口的子任務(wù)生成的Java源文件需要放在不同的位置,所以覆蓋了destdir的屬性值。布署描述符文件需要放在一個在打包EJB JAR文件的時候可以容易包含進(jìn)來的地方,而生成的Java代碼則需要放置在一個可以調(diào)用Java編譯器進(jìn)行編譯的地方。需要這些子任務(wù)之間是緊密關(guān)聯(lián)的,但只要你需要,你可以有足夠的自主權(quán)控制任務(wù)的生成環(huán)境。
          <fileset>屬性同樣被應(yīng)用到所有的子任務(wù)。這是一個Ant的復(fù)雜類型(相對于文本和數(shù)值的簡單類型),所以以子元素的方式在任務(wù)中聲明。不要把它和子任務(wù)混為一談。當(dāng)然,如果你想在某個子任務(wù)中另外指定一個不同的輸入文件集,你也可以在這個子任務(wù)中放置一個<fileset>子元素來覆蓋它。
          子任務(wù)的可配置選項(xiàng)遠(yuǎn)遠(yuǎn)不止這些。我們會在下一章繼續(xù)介紹所有的任務(wù)和子任務(wù),以及常用的配置選項(xiàng)。

          2.4 用屬性標(biāo)注你的代碼
          可重用的代碼生成系統(tǒng)需要輸入來生成感興趣的輸出。一個解析器生成器也需要一個語言描述來解析生成解析器。一個商務(wù)對象代碼生成器需要領(lǐng)域模型來知道要生成哪些商務(wù)對象。XDoclet則需要Java源文件做為輸出來生成相關(guān)的類或者布署/配置文件。
          然而,源文件可能并沒有提供代碼生成所需要的所有信息??紤]一個基于servlet的應(yīng)用,當(dāng)你想生成web.xml文件的時候,servlet源文件僅可以提供類名和適當(dāng)?shù)膕ervlet接口方法。其他的信息比如URI pattern映射、servlet需要的初始化參數(shù)等信息并沒有涵蓋。顯而易見,如果class并沒有提供這些信息給你,你就需要自己手動在web.xml文件時填寫這些信息。
          XDoclet當(dāng)然也不會知道這些信息。幸運(yùn)的是,解決方法很簡單。如果所需信息在源文件時沒有提供,那就提供它,做法就是在源文件里加入一些XDoclet屬性。XDoclet解析源文件,提取這些屬性,并把它們傳遞給模板,模板使用這些信息生成代碼。

          2.4.1 剖析屬性
          XDoclet屬性其實(shí)就是javadoc的擴(kuò)展。它們在外表上和使用上都有javadoc屬性一樣,可以放置在javadoc文檔注釋里。文檔注釋以/**開始,*/結(jié)尾。下面是一個簡單的例子:
          /**
          * 這是一段javadoc注釋。
          * 注釋可以被分解成多行,每一行都以星號開始。
          */
          在注釋里的所有文本都被視為javadoc注釋,并且都能夠被XDoclet訪問到。注釋塊一般都與Java源文件中的某個實(shí)體有關(guān),并緊跟在這個實(shí)體的前面。沒有緊跟實(shí)體的注釋塊將不會被處理。類(或者接口)可以有注釋塊,方法和域也可以有自己的注釋塊,比如:
          /**
          * 類注釋塊
          */
          public class SomeClass {
            /** 域注釋塊 */
            private int id;
            /**
          * 構(gòu)造函數(shù)注釋塊
          */
            public SomeClass() {
              // ...
            }
            /**
             * 方法注釋塊
             */
            public int getId() {
              return id;
            }
          }
          注釋塊分成兩部分:描述部分和標(biāo)簽部分。當(dāng)遇到第一個javadoc標(biāo)簽時,標(biāo)簽部分開始。Javadoc標(biāo)簽也分成兩部分:標(biāo)簽名和標(biāo)簽描述。標(biāo)簽描述是可選的,并且可以多行。例如:
          /**
          * 這是描述部分
          * @tag1 標(biāo)簽部分從這里開始
          * @tag2
          * @tag3 前面一個標(biāo)簽沒有標(biāo)簽描述。
          * 這個標(biāo)簽有多行標(biāo)簽描述。
          */
          XDoclet使用參數(shù)化標(biāo)簽擴(kuò)展了javadoc標(biāo)簽。在XDoclet里,你可以在javadoc標(biāo)簽的標(biāo)簽描述部分加入name=”value”參數(shù)。這個微小的改動大大增強(qiáng)了javadoc標(biāo)簽的表達(dá)能力,使得javadoc標(biāo)簽可以用來描述復(fù)雜的元數(shù)據(jù)。下面的代碼顯示了使用XDoclet屬性描述實(shí)體Bean方法:
          /**
          * @ejb.interface-method
          * @ejb.relation
          * name=”blog-entries”
          * role-name=”blog-has-entries”
          * @ejb.value-object
          * compose=”com.xdocletbook.blog.value.EntryValue”
          * compose-name=”Entry”
          * members=”com.xdocletbook.blog.interfaces.EntryLocal”
          * members-name=”Entries”
          * relation=”external”
          * type=”java.util.Set”
          */
          public abstract Set getEntries();
          參數(shù)化標(biāo)簽允許組合邏輯上相關(guān)聯(lián)的屬性。你可以加入描述這個類的元信息,使得這個類的信息足夠生成代碼。另外,程序員借由閱讀這樣的元信息,可以很快的理解這個類是如何使用的。(如果這個例子上的元信息你看不懂,不要擔(dān)心,在第4章里,我們會學(xué)到EJB相關(guān)的標(biāo)簽以及它們的涵意。)
          另外,請注意上面的例子中,所有的標(biāo)簽名都以ejb開頭。XDoclet使用namespace.tagname的方式給標(biāo)簽提供了一個命名空間。這樣做除了可以跟javadoc區(qū)別開來以外,還可以把任務(wù)相關(guān)的標(biāo)簽組織起來,以免任務(wù)之間的標(biāo)簽產(chǎn)生混淆。 

          2.5 代碼生成模式
          XDoclet是一種基于模板的代碼生成引擎。從高層視圖上來看,輸出文件其實(shí)就是由解析執(zhí)行各式各樣的模板生成出來的。如果你理解了模板以及它所執(zhí)行的上下文環(huán)境,就可以確切的認(rèn)識到,XDoclet可以生成什么,不可以生成什么。如果你正在評估XDoclet平臺,理解這些概念是非常重要的。要不然,你可能會錯過XDoclet的許多強(qiáng)大的功能,也可能會被XDoclet的一些限制感到迷惑。
          XDoclet運(yùn)行在在Ant構(gòu)建文件環(huán)境中,它提供了Ant自定義任務(wù)和子任務(wù)來與XDoclet引擎交互。任務(wù)是子任務(wù)的容器,子任務(wù)負(fù)責(zé)執(zhí)行代碼生成。子任務(wù)調(diào)用模板。模板提供了你將生成代碼的餅干模子。XDoclet解析輸入的源文件,提取出源文件中的XDoclet屬性元數(shù)據(jù),再把這些數(shù)據(jù)提供給模板,驅(qū)動模板執(zhí)行。除此之外,模板還可以提供合并點(diǎn)(merge points),允許用戶插入一些模板片斷(合并文件merge files)來根據(jù)需要定制代碼生成。
          2.5.1 模板基礎(chǔ)
          XDoclet使用代碼模板來生成代碼。模板(template)是你想生成文件的原型。模板里使用一些XML標(biāo)簽來指導(dǎo)模板引擎如何根據(jù)輸入類以及它們的元數(shù)據(jù)來調(diào)整代碼的生成。
          [定義:模板(template)是生成代碼或描述文件的抽象模視圖。當(dāng)模板被解析的時候,指定的細(xì)節(jié)信息會被填入。]
          模板一般情況下會有一個執(zhí)行環(huán)境。模板可能應(yīng)用在一個類環(huán)境(轉(zhuǎn)換生成transform generation),也有可能應(yīng)用在一個全局環(huán)境(聚集生成aggregate generation)。轉(zhuǎn)換生成和聚集生成是XDoclet的兩種類型的任務(wù)模式,理解它們之間的區(qū)別對于理解XDoclet是非常重要的。
          當(dāng)你使用XDoclet生成布置描述符文件時,你使用的是聚集生成。布置描述符文件并不僅僅只與一個類相關(guān),相反,它需要從多個類里聚集信息到一個輸入文件。在這種生成模式里,解析一次模板只會生成一個輸出文件,不管有多少個輸入文件。
          在轉(zhuǎn)換生成模式里,模板遇到每一個源文件就會解析一次,根據(jù)該文件類的上下文環(huán)境生成輸出。這種生成模式會為每一個輸入文件生成一個輸出文件。
          轉(zhuǎn)換生成模式的一個很好的例子是生成EJB的local和remote接口。顯然,接口是和Bean類一一相關(guān)的。從每一個類里提取信息(類以及它的方法、域、接口以及XDoclet屬性等信息)轉(zhuǎn)換出接口。除此以外,不需要其他的信息。
          從實(shí)現(xiàn)里提取出接口似乎有點(diǎn)反向。如果你手寫程序的話,一般來說會先定義一個接口,然后再寫一個類來關(guān)現(xiàn)它。但XDoclet做不到,XDoclet不可能幫你實(shí)現(xiàn)一個已有接口,因?yàn)樗豢赡軒湍闵赡愕臉I(yè)務(wù)邏輯。當(dāng)然,如果業(yè)務(wù)邏輯可以從接口本身得到(比如JavaBean的get/set訪問器)或者使用XDoclet屬性聲明好,那么生成業(yè)務(wù)邏輯代碼來實(shí)現(xiàn)一個接口也不是不可能。但一般情況下,這樣做不太現(xiàn)實(shí)。相比而言,提供一個實(shí)現(xiàn),并描述接口與這個實(shí)現(xiàn)之間的關(guān)聯(lián)就容易多了。
          聚集生成和轉(zhuǎn)換生成主要區(qū)別在它們的環(huán)境信息上。即使一個代碼生成任務(wù)中生成一個Java文件,一般也不常用聚集生成,因?yàn)樯梢粋€Java類還需要一些重要信息如類所處的包以及你想生成的類名,在這種環(huán)境下是無法提供的。如果一定要使用聚集生成的話,那就需要在另一個單獨(dú)的地方提供好配置信息了。
          2.5.2 模板標(biāo)簽
          在還沒見到模板長啥樣子之前,我們已經(jīng)比較深入的認(rèn)識它了。那模板文件究竟長啥樣子呢?它有點(diǎn)像JSP文件。它們都包含文件和XML標(biāo)簽,生成輸出文件時XML標(biāo)簽會被解析,然后生成文本并顯示在XML標(biāo)簽所處的位置上。除了以XDt為命名空間打頭的XML標(biāo)簽會被XDoclet引擎解析以外,其余的XML標(biāo)簽XDoclet會忽略不管。下面的代碼片斷顯示了XDoclet模板的“經(jīng)典造型”:
            public class
              <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
              Extends Observabe {
              static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
                _instance = null;
              public static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClassOf>
                getInstance() {
                if (_instance == null) {
                  _instance =
          new <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/>
                    </XDtClass:classOf>();
               ?。?br />       return _instance;
          }
          }
          研究一下這個模板,你會發(fā)現(xiàn),它生成的是一個類定義。這個類里定義了一個靜態(tài)變量instance,并且使用一個靜態(tài)方法來控制這個靜態(tài)文件的訪問。借助Java語法,你可以很容易的推斷出那些XDoclet模板標(biāo)簽的目錄是生成類名,雖然對于這個標(biāo)簽如何工作你還并不是很了解。
          即使你從沒打算過要自己寫模板,但理解模板是如何被解析運(yùn)行的還是很有必要的。遲早你會調(diào)用到一個運(yùn)行失敗的XDoclet任務(wù),沒有產(chǎn)生你所期望的輸出,那么最快捷的找出原因的方法就是直接檢查模板文件,看看是哪里出了問題。
          讓我們看一下生成靜態(tài)域定義的片斷:
          static <XDtClass:classOf><XDtEjbFacade:remoteFacadeClass/></XDtClass:classOf>
              _instance = null;
          在XDoclet的眼里,這段模板代碼很簡單,那就是:
            static <tag/> _instance = null;
          XDoclet解析執(zhí)行標(biāo)簽,如果有輸出的話,把輸入置回到文本里去。有些標(biāo)簽會執(zhí)行一些運(yùn)算,把輸出放回到一個流里。這樣的標(biāo)簽稱之為內(nèi)容標(biāo)簽(content tags),因?yàn)樗鼈儺a(chǎn)生內(nèi)容。
          另一種類型的標(biāo)簽稱之為BODY標(biāo)簽。BODY標(biāo)簽在開始和結(jié)束標(biāo)簽之間存在文本。而BODY標(biāo)簽強(qiáng)大就強(qiáng)大在這些文本自己也可以是一斷可以由外圍標(biāo)簽解析的模板片斷。比如在上面的例子里,XDtClass:classOf標(biāo)簽,它們的內(nèi)容就是模板片斷:
            <XDtEjbFacade:remoteFacadeClass/>
          classOf標(biāo)簽解析這段模板,提取出全限制的內(nèi)容,然后剃除前面的包面,只輸出類名。BODY標(biāo)簽并不總是會解析它的內(nèi)容,在做這件事之前,它們會事先檢查一些外部判斷條件(比如檢查檢查你正在生成的是一個接口還是一個類)。這里標(biāo)標(biāo)簽稱之為條件標(biāo)簽(conditional tags)。還有一些BODY標(biāo)簽提供類似迭代的功能,它的內(nèi)容會被解析多次。比如一個標(biāo)簽針對類里的每一個方法解析一次內(nèi)容。
          XDoclet標(biāo)簽提供了許多高層次的代碼生成功能,但是有時候,它們可能顯得不夠靈活,或者表達(dá)能力滿足不了你的需要。這時候,相對于另外開發(fā)一套通用功能的模板引擎相比,你可以選擇擴(kuò)展XDoclet模板引擎。你可以使用更具表述能力、功能更加強(qiáng)大的Java平臺開發(fā)你自己的一套標(biāo)簽。 

          2.6 使用合并定制
          代碼生成系統(tǒng)之所以使用的不多,主要原因就在于它們往往只能生成一些死板的、不夠靈活的代碼。大多數(shù)代碼生成系統(tǒng)不允許你改動它們生成的代碼;如果,如果這個系統(tǒng)不夠靈活,你所能做到的最好的擴(kuò)展就是應(yīng)用繼承擴(kuò)展生成的代碼,或者使用一些共通的設(shè)計模式(比如Proxy和Adaptor)來滿足你的需要。無論如此,這都不是產(chǎn)生你想生成的代碼的好辦法。代碼生成器最好能做到所生成即所得WYGIWYG(what you generate is what you get),來取代你需要花費(fèi)大量的時間來粉飾生成出來的并不滿足要求的代碼。所以,對于代碼生成器來說,支持靈活的定制,是生成能夠完全滿足要求的代碼的前提條件。
          XDoclet通過合并點(diǎn)(merge points)支持定制??合并點(diǎn)是在模板文件定義里允許運(yùn)行時插入定制代碼的地方。有時候,合并點(diǎn)甚至可以影響到全局代碼的生成,不但允許你添加一些定制內(nèi)容,還可以從根本上改變將要生成出來的東西。
          [定義:合并點(diǎn)(Merge points)是模板預(yù)先定義的允許你在代碼生成的運(yùn)行時加入定制內(nèi)容的擴(kuò)展點(diǎn)]
          讓我們研究一段從XDoclet源代碼里摘取出來的模板代碼。在為實(shí)體Bean生成主鍵的模板末尾,定義了這樣一個合并點(diǎn):
            <XDtMerge:merge file=”entitypk-custom.xdt”></XDtMerge:merge>
          如果你在你的merge目錄下創(chuàng)建了一個名為entitypk-custom.xdt文件,那么這個模板文件的內(nèi)容將會在這個合并點(diǎn)被包含進(jìn)來。你的定制可以執(zhí)行高層模板可以執(zhí)行的所有強(qiáng)大功能,可以進(jìn)行所有模板可以進(jìn)行的運(yùn)算(包括定義自定義標(biāo)簽,定義它們自己的合并點(diǎn))。
          上面的這種合并點(diǎn),為所有的類環(huán)境使用了同一個文件。當(dāng)然,也可以為每一個類環(huán)境使用不同的合并文件。如果你不想定制全部的類文件,或者你不想為了某些改動而重寫模板的時候,這會很有用。不管動機(jī)是什么,逐類的合并點(diǎn)很容易識別出來:他們會在名字里包含一個XDoclet的逐類標(biāo)記{0}。這里有一個生成ejb-jar.xml文件里的安全角色引用的例子:
            <XDtMerge:merge file=”ejb-sec-rolerefs-{0}.xml”>
              <XDtClass:forAllClassTags tagName=”ejb:security-role-ref”>
                <security-role-ref>
                  <role-name>
          <XDtClass:classTagValue
          tagName=”ejb:security-roleref”
          paramName=”role-name”/>
                  </role-name>
                  <role-link>
                    <XDtClass:classTagValue
                      tagName=”ejb:security-roleref”
                      paramName=”role-link”/>
                  </role-link>
                </security-role-ref>
              </XDtClass:forAllClassTags>
            </XDtMerge:merge>
          這段模板會遍歷工程里的所有Bean。對于每一個Bean,XDoclet先從Bean的文件名里提取出Bean名,然后替換{0},再根據(jù)替換后的文件名去尋找合并文件。例如,如果你有一個名為BlogFacadeBean的Bean,XDoclet會嘗試尋找一個名為ejb-src-rolerefs-BlogFacade.xml的合并文件。
          如果找不到這個合并文件,則這個<merge>標(biāo)簽的內(nèi)容模板會被解析。這意味著合并點(diǎn)不僅可以提供定制內(nèi)容,還可以在一個模板文件里定義一個替換點(diǎn),當(dāng)定制內(nèi)容不存在的時候使用替換點(diǎn)里的內(nèi)容。不是所有的XDoclet任務(wù)都提供了有替換內(nèi)容的合并點(diǎn),一般來說,它們更傾向于只提供一個簡單的合并點(diǎn),僅僅當(dāng)合并文件存在的時候解析并導(dǎo)入合并文件的內(nèi)容。這取決于任務(wù)的開發(fā)者覺得哪種合并點(diǎn)更符合他的要求。
          還有一點(diǎn)沒有介紹到的是XDoclet如何定位合并文件,每一個XDoclet任務(wù)或者子任務(wù)都會提供一個mergeDir屬性,這個屬性用于設(shè)置你存放合并文件的目錄。 

          posted @ 2007-09-30 15:59 夏雪 閱讀(1014) | 評論 (0)編輯 收藏

          EJB技術(shù)

           一、EJB技術(shù)簡介
          EJB的全稱是Enterprise java bean。是JAVA中的商業(yè)應(yīng)用組件技術(shù)。EJB結(jié)構(gòu)中的角色 EJB 組件結(jié)構(gòu)是基于組件的分布式計算結(jié)構(gòu),是分布式應(yīng)用系統(tǒng)中的組件。
          一個完整的基于EJB的分布式計算結(jié)構(gòu)由六個角色組成,這六個角色可以由不同的開發(fā)商提供,每個角色所作的工作必須遵循Sun公司提供的EJB規(guī)范,以保證彼此之間的兼容性。這六個角色分別是EJB組件開發(fā)者(Enterprise Bean Provider) 、應(yīng)用組合者(Application Assembler)、部署者(Deployer)、EJB 服務(wù)器提供者(EJB Server Provider)、EJB 容器提供者(EJB Container Provider)、系統(tǒng)管理員(System Administrator):
          二、EJB中各角色的分析
          1、EJB組件開發(fā)者(Enterprise Bean Provider)
          EJB組件開發(fā)者負(fù)責(zé)開發(fā)執(zhí)行商業(yè)邏輯規(guī)則的EJB組件,開發(fā)出的EJB組件打包成ejb-jar文件。EJB組件開發(fā)者負(fù)責(zé)定義EJB的remote和home接口,編寫執(zhí)行商業(yè)邏輯的EJB class,提供部署EJB的部署文件(deployment descriptor)。部署文件包含EJB的名字,EJB用到的資源配置,如JDBC等。EJB組件開發(fā)者是典型的商業(yè)應(yīng)用開發(fā)領(lǐng)域?qū)<摇?br /> EJB組件開發(fā)者不需要精通系統(tǒng)級的編程,因此,不需要知道一些系統(tǒng)級的處理細(xì)節(jié),如事務(wù)、同步、安全、分布式計算等。
          2、應(yīng)用組合者(Application Assembler)
          應(yīng)用組合者負(fù)責(zé)利用各種EJB組合一個完整的應(yīng)用系統(tǒng)。應(yīng)用組合者有時需要提供一些相關(guān)的程序,如在一個電子商務(wù)系統(tǒng)里,應(yīng)用組合者需要提供JSP(Java Server Page)程序。
          應(yīng)用組合者必須掌握所用的EJB的home和remote接口,但不需要知道這些接口的實(shí)現(xiàn)。
          3、部署者(Deployer)
          部署者負(fù)責(zé)將ejb-jar文件部署到用戶的系統(tǒng)環(huán)境中。系統(tǒng)環(huán)境包含某種EJB Server和EJB Container。部署者必須保證所有由EJB組件開發(fā)者在部署文件中聲明的資源可用,例如,部署者必須配置好EJB所需的數(shù)據(jù)庫資源。
          部署過程分兩步:部署者首先利用EJB Container提供的工具生成一些類和接口,使EJB Container能夠利用這些類和接口在運(yùn)行狀態(tài)管理EJB。 部署者安裝EJB組件和其他在上一步生成的類到EJB Container中。 部署者是某個EJB運(yùn)行環(huán)境的專家。
          某些情況下,部署者在部署時還需要了解EJB包含的業(yè)務(wù)方法,以便在部署完成后,寫一些簡單的程序測試。
          4、EJB 服務(wù)器提供者(EJB Server Provider)
          EJB 服務(wù)器提供者是系統(tǒng)領(lǐng)域的專家,精通分布式交易管理,分布式對象管理及其它系統(tǒng)級的服務(wù)。EJB 服務(wù)器提供者一般由操作系統(tǒng)開發(fā)商、中間件開發(fā)商或數(shù)據(jù)庫開發(fā)商提供。
          在目前的EJB規(guī)范中,假定EJB 服務(wù)器提供者和EJB 容器提供者來自同一個開發(fā)商,所以,沒有定義EJB 服務(wù)器提供者和EJB容器提供者之間的接口標(biāo)準(zhǔn)。
          5、EJB 容器提供者(EJB Container Provider)
          EJB 容器提供者提供以下功能:
          提供EJB部署工具為部署好的EJB組件提供運(yùn)行環(huán)境 。EJB容器負(fù)責(zé)為EJB提供交易管理,安全管理等服務(wù)。
          EJB 容器提供者必須是系統(tǒng)級的編程專家,還要具備一些應(yīng)用領(lǐng)域的經(jīng)驗(yàn)。EJB 容器提供者的工作主要集中在開發(fā)一個可伸縮的,具有交易管理功能的集成在EJB 服務(wù)器中的容器。EJB 容器提供者為EJB組件開發(fā)者提供了一組標(biāo)準(zhǔn)的、易用的API訪問EJB 容器,使EJB組件開發(fā)者不需要了解EJB服務(wù)器中的各種技術(shù)細(xì)節(jié)。
          EJB容器提供者負(fù)責(zé)提供系統(tǒng)監(jiān)測工具用來實(shí)時監(jiān)測EJB容器和運(yùn)行在容器中的EJB組件狀態(tài)。
          6、系統(tǒng)管理員(System Administrator)
          系統(tǒng)管理員負(fù)責(zé)為EJB服務(wù)器和容器提供一個企業(yè)級的計算和網(wǎng)絡(luò)環(huán)境。
          系統(tǒng)管理員負(fù)責(zé)利用EJB 服務(wù)器和容器提供的監(jiān)測管理工具監(jiān)測EJB組件的運(yùn)行情況。
          三、EJB的體系結(jié)構(gòu):
          EJB分布式應(yīng)用程序是基于對象組件模型的,低層的事務(wù)服務(wù)用了API技術(shù)。EJB技術(shù)簡化了用JAVA語言編寫的企業(yè)應(yīng)用系統(tǒng)的開發(fā),配置。EJB技術(shù)定義了一組可重用的組件:Enterprise Beans。你可以利用這些組件,象搭積木一樣的建立你的分布式應(yīng)用程序。當(dāng)你把代碼寫好之后,這些組件就被組合到特定的文件中去。每個文件有一個或多個Enterprise Beans,在加上一些配置參數(shù)。最后,這些Enterprise Beans被配置到一個裝了EJB容器的平臺上??蛻裟軌蛲ㄟ^這些Beans的home接口,定位到某個beans,并產(chǎn)生這個beans的一個實(shí)例。這樣,客戶就能夠調(diào)用Beans的應(yīng)用方法和遠(yuǎn)程接口。
          EJB服務(wù)器作為容器和低層平臺的橋梁管理著EJB容器和函數(shù)。它向EJB容器提供了訪問系統(tǒng)服務(wù)的能力。例如:數(shù)據(jù)庫的管理和事務(wù)的管理,或者對于其它的Enterprise的應(yīng)用服務(wù)器。所有的EJB 實(shí)例都運(yùn)行在EJB容器中。容器提供了系統(tǒng)級的服務(wù),控制了EJB的生命周期。EJB中的有一些易于使用的管理工具如:Security--配置描述器(The Deployment descriptor)定義了客戶能夠訪問的不同的應(yīng)用函數(shù)。容器通過只允許授權(quán)的客戶訪問這些函數(shù)來達(dá)到這個效果。Remote Connectivity--容器為遠(yuǎn)程鏈接管理著低層的通信issues,而且對Enterprise Beas的開發(fā)者和客戶都隱藏了通信細(xì)節(jié)。EJB的開發(fā)者在編寫應(yīng)用方法的時候,就象是在條用本地的平臺一樣的。客戶也不清楚他們調(diào)用的方法可能是在遠(yuǎn)程被處理的。Life Cycle managment--客戶簡單的創(chuàng)建一個Enterprise beans的實(shí)例,并通常取消一個實(shí)例。而容器管理著Enterprise Beans的實(shí)例,使Enterprise Beans實(shí)現(xiàn)最大的效能和內(nèi)存利用率。容器能夠這樣來激活和使Enterprise Beans失效,保持眾多客戶共享的實(shí)例池。等等?! rasction management-配置描述器定義了Enterprise beans 的事務(wù)處理的需求。容器管理著那些管理分布式事務(wù)處理的復(fù)雜的issues。這些事務(wù)可能要在不同的平臺之間更新數(shù)據(jù)庫。容器使這些事務(wù)之間互相獨(dú)立,互不干擾。保證所有的更新數(shù)據(jù)庫都是成功發(fā)生的,否者,就回滾到事務(wù)處理之前的狀態(tài)。
          EJB 組件是基于分布式事務(wù)處理的企業(yè)級應(yīng)用程序的組件。所有的EJB都有如下的特點(diǎn):EJB包含了處理企業(yè)數(shù)據(jù)的應(yīng)用邏輯。定義了EJB的客戶界面。這樣的界面不受容器和服務(wù)器的影響。于是,當(dāng)一個EJB被集合到一個應(yīng)用程序中去時,不用更改代碼和重新編譯。EJB能夠被定制 各種系統(tǒng)級的服務(wù),例如安全和事務(wù)處理的特性,都不是屬于EJB類的。而是由配置和組裝應(yīng)用程序的工具來實(shí)現(xiàn)。 有兩種類型的EJB: Session beans 和 entity beans.Session beans是一種作為單用戶執(zhí)行的對象。作為對遠(yuǎn)程的任務(wù)請求的相應(yīng),容器產(chǎn)生一個Session beans 的實(shí)例。一個Session beans有一個用戶.從某種程度上來說,一個Session bean 對于服務(wù)器來說就代表了它的那個用戶.Session beans 也能用于事務(wù),它能夠更新共享的數(shù)據(jù),但它不直接描繪這些共享的數(shù)據(jù)。Session beans 的生命周期是相對較短的。典型的是,只有當(dāng)用戶保持會話的時候,Session beans 才是活著的。一旦用戶退出了,Session beans 就不再與用戶相聯(lián)系了。Session beans被看成是瞬時的,因?yàn)槿绻萜鞅罎⒘?,那么用戶必須重新建立一個新的Session對象來繼續(xù)會話。
          Session bean典型的聲明了與用戶的互操作或者會話。也就是說,Session bean了在客戶會話期間,通過方法的調(diào)用,掌握用戶的信息。一個具有狀態(tài)的Session bean稱為有狀態(tài)的Session bean.當(dāng)用戶終止與Session beans互操作的時候.會話終止了,而且,bean 也不再擁有狀態(tài)值。Session bean也可能是一個無狀態(tài)的 session bean.無狀態(tài)的Session beans并不掌握它的客戶的信息或者狀態(tài)。用戶能夠調(diào)用beans的方法來完成一些操作。但是,beans只是在方法調(diào)用的時候才知道用戶的參數(shù)變量。當(dāng)方法調(diào)用完成以后,beans并不繼續(xù)保持這些參數(shù)變量。這樣,所有的無狀態(tài)的session beans的實(shí)例都是相同的,除非它正在方法調(diào)用期間。這樣,無狀態(tài)的Session beans就能夠支持多個用戶.容器能夠聲明一個無狀態(tài)的Session beans.能夠?qū)⑷魏蜸ession beans指定給任何用戶.
          Entity Beans對數(shù)據(jù)庫中的數(shù)據(jù)提供了一種對象的視圖。例如:一個Entity bean能夠模擬數(shù)據(jù)庫表中一行相關(guān)的數(shù)據(jù)。多個client能夠共享訪問同一個Entity bean.多個client也能夠同時的訪問同一個Entity bean.Entity beans通過事務(wù)的上下文來訪問或更新下層的數(shù)據(jù)。這樣,數(shù)據(jù)的完整性就能夠被保證。Entity Beans能存活相對教長的時間,并且狀態(tài)是持續(xù)的。只要數(shù)據(jù)庫中的數(shù)據(jù)存在,Entity beans就一直存活。而不是按照應(yīng)用程序或者服務(wù)進(jìn)程來說的。即使EJB容器崩潰了,Entity beans也是存活的。Entity Beans生命周期能夠被容器或者 Beans自己管理。如果由容器控制著保證 Entity beans持續(xù)的issus。如果由Beans自己管理,就必須寫Entity beans的代碼,包括訪問數(shù)據(jù)庫的調(diào)用。
          Entity Beans是由主鍵(primary key 一種唯一的對象標(biāo)識符)標(biāo)識的。通常,主鍵與標(biāo)識數(shù)據(jù)庫中的一塊數(shù)據(jù),例如一個表中的一行,的主鍵是相同的。主鍵是client能夠定位特定的數(shù)據(jù)塊。
          四、開發(fā)EJB
          1、類介紹:
          開發(fā)EJB的主要步驟一般來說,整個的開發(fā)步驟(開發(fā),配置,組裝)包括如下幾個方面。開發(fā):首先要定義三個類:Bean類本身,Bean的本地和遠(yuǎn)程接口類。 配置:配置包括產(chǎn)生配置描述器--這是一個XML文件、聲明了Enterprise Bean的屬性、綁定了bean的class文件(包括stub文件和skeleton文件)。最后將這些配置都放到一個jar文件中。還需要在配置器中定義環(huán)境屬性。組裝應(yīng)用程序:包括將Enterprise beans安裝到Server服務(wù)器中,測試各層的連接情況。程序組裝器將若干個Enterprise Beans與其它的組件結(jié)合起來。組合成一個完整的應(yīng)用程序?;蛘邔⑷舾蓚€Enterprise beans組合成一個復(fù)雜的Enterprise Bean。管理Enterprise Bean。
          我們必須定義和編寫一些EJB中的基本類。如Enterprise bean類:這是Enterprise bean內(nèi)部應(yīng)用邏輯的實(shí)現(xiàn)。編寫Enterprise bean的遠(yuǎn)程接口類。編寫Enterprise bean的本地接口類。說明主鍵類,主鍵類只是對于Entity bean才需要的。在Enterprise bean的配置描述器中指定主鍵的名字。Enterprise beans提供者定義了遠(yuǎn)程接口和本地接口,實(shí)現(xiàn)了EJB類本身。Remote接口中提供了客戶調(diào)用EJB實(shí)現(xiàn)的應(yīng)用邏輯函數(shù)的接口。而home接口提供了產(chǎn)生和定位remote接口實(shí)例的方法。
          在Enterprise bean本身類的實(shí)現(xiàn),本地home接口,遠(yuǎn)程remote接口之間并沒有正式的聯(lián)系(例如繼承關(guān)系)。但是,在三個類里聲明的方法卻必須遵守EJB里面定義的規(guī)范。例如: 你在Enterprise bean里面聲明了一個應(yīng)用程序的方法或者說應(yīng)用邏輯。也在beans的remote接口中聲明了這個方法,那么,這兩個地方必須要同樣的名字。Bean的實(shí)現(xiàn)里面必須至少有一個Create()方法:ejbCreate()。但是可以有多個帶有不同參數(shù)的create()方法?! ≡趆ome接口中,也必須有相同的方法定義(參數(shù)的個數(shù)相同)。EjbCreate()方法返回的一個容器管理的持久對象。它們都返回一個容器管理持久性的主鍵值。但是,在home的相應(yīng)的Create()方法中返回值的類型是remote接口。
          注意:實(shí)體bean的實(shí)現(xiàn)的ejbCreate方法有點(diǎn)不同。實(shí)體bean可以不定義ejbCreate方法。如果實(shí)體只是通過應(yīng)用程序或通過數(shù)據(jù)庫管理程序的途徑被加到數(shù)據(jù)庫中,實(shí)體bean就省略了ejbCreate方法。EjbCreate返回的值是主鍵類型。如果ejbCreate方法是容器管理持久性的實(shí)體bean的方法,它的返回值就是NULL類型。如果實(shí)體bean實(shí)現(xiàn)了Bean管理的持久性,ejbCreate方法就返回值類型就是主鍵類型。容器的任務(wù)是把各接口和Enterprise bean的實(shí)現(xiàn)類結(jié)合起來。保證在編譯時和運(yùn)行時,各接口和實(shí)現(xiàn)類是相對應(yīng)的。
          EJB的實(shí)現(xiàn)類,各接口要從不同的基類中繼承下來。一個會話bean必須實(shí)現(xiàn)基類javax.ejb.SessionBean。而實(shí)體bean必須實(shí)現(xiàn)基類javax.ejb.EntiyBean。這些EJB的基類都是從javax.ejb.EnterpriseBean繼承而來。而javax.ejb.EnterpriseBean又是從java.io.Serializable繼承而來。每一個Enterprise Bean都必須有一個remote接口。Remote接口定義了應(yīng)用程序規(guī)定客戶可以調(diào)用的邏輯操作。這些是一些可以由客戶調(diào)用的公共的方法,通常由Enterprise beans類來實(shí)現(xiàn)。注意,Enterprise bean的客戶并不直接訪問Bean。而是通過remote接口來訪問。Enterprise bean類的remote接口擴(kuò)展了javax.ejb.EJBObject類的公共java接口。而Javax.ejb.EJBObject是所有remote接口的基類。其代碼如下:
          package javax.ejb;
          public interface EJBObject extends java.rmi.Remote{
          public EJBHome getEJBHome() throws java.rmi.RemoteException;
          public Object getPrimaryKey() throws java.rmi.RemoteException;
          public void Remove() throws java.rmi.RemtoeException, java.rmi.RemoveException
          public Handle getHandle() throws java.rmi.RemoteException;
          boolean isIdentical (EJBObject p0) throws java.rmi.RemoteException;
          }
          getEJBHome()方法允許你取得一個相關(guān)的Home接口。對于 實(shí)體Bean,用getPrimaryKey()方法獲得實(shí)體Bean的主鍵值。Remove()可以刪除一個Enterprise bean。具體的語義在各種不同類型的enterprise beans的生命周期中,由上下文中解釋的。方法getHandle()返回了一個Enterprise bean實(shí)例的持久的句柄。IsIndentical()方法允許你去比較Enterprise beans是否相同。
          2、方法:
          所有的remote接口中的方法必須聲明為公共(public)的,并必須拋出java.rmi.RemotException異常。另外,所有的remote接口中的方法定義的參數(shù)和都必須是在RMI-IIOP中有效的。對每一個在remote接口中定義的方法,在Enterprise bean 類里面都要有相應(yīng)的方法。相應(yīng)的方法必須要有同樣的名字,同樣類型和數(shù)量的參數(shù),同樣的返回值,而且還要拋出同樣的例外。 如下代碼顯示了一個ATM例子的會話bean的remote接口Atm,。里面聲明了一個應(yīng)用方法transfer()。黑體部分表示EJB規(guī)范中必須要有的內(nèi)容。Remote接口必須擴(kuò)展javax.ejb.EJBObject類。從客戶端調(diào)用的Enterprise bean的每一個方法都必須在remote接口中聲明。Transfer()方法拋出了兩個意外。其中InSufficientFundsException例外是應(yīng)用程序定義的意外。
          Public interface Atm extends javax.ejb.EJBObject{
          Public void transfer(String Source, String Target, float amount)
          Throws java.rmi.RemoteException, InSufficientFundsException;
          }
          Home接口必須定義一個或多個的Create()方法。每一個這樣的Create()方法都必須命名為Create。并且,它的參數(shù),不管是類型還是數(shù)量都必須與bean類里面的ejbCreate()方法對應(yīng)。注意,home接口中的Create()方法和bean類中ejbCreate()方法的返回值類型是不同的。實(shí)體bean的home接口還包含find()方法。 每一個Home接口都擴(kuò)展了javax.ejb.EJBHome接口。如下代碼顯示了javax.ejb.EJBHome接口的定義:
          package javax.ejb;
          public interface EJBHome extends java.rmi.Remote() {
          void remove(Handle handle) throws java.rmi.RemoteException,RemoveException;
          void remove(Object primarykey) throws java.rmi.RemoteException,RemoveException;
          EJBMetaData getEJBMetaData() throws RemoteException;
          Homehandle getHomeHandle() throws RemoteException;
          }
          這里提供了兩個remove()方法來刪除Enterprise bean的實(shí)例。第一個remove方法是通過句柄來刪除一個Enterprise bean的實(shí)例。第二個remove方法通過主鍵來刪除一個Enterprise bean的實(shí)例。 在眾多的Enterprise bean實(shí)例中,句柄唯一的標(biāo)識一個實(shí)例。一個句柄與它引用的Enterprise bean有相同的生命期??紤]一個實(shí)體對象,客戶可以通過一個句柄來重新獲得相應(yīng)的Enterprise bean的實(shí)例。一個句柄能夠?qū)?yīng)一個Enterprise bean對象的多個實(shí)例。例如,即使當(dāng)Enterprise bean對象所在的主機(jī)崩潰了,或者Enterprise bean對象在不同的機(jī)器之間移動,句柄仍是有效的。這里的句柄是Serialized句柄,與CORBA中的字符串化的CORBA對象的引用是相似的概念。在EJBHome接口中的第二個remove操作通過其主鍵來決定要刪除的Enterprise bean。主鍵可以是擴(kuò)展了Java Object類的任何類型,但是,必須要實(shí)現(xiàn)Java的Serializable接口。主鍵是標(biāo)識實(shí)體bean的主要的方法。通常,主鍵是數(shù)據(jù)庫中的一個關(guān)鍵字,唯一的定義了由實(shí)體bean代表的數(shù)據(jù)。
          方法getEJBMetaData()返回了Enterprise bean對象的metadata接口。這個接口允許客戶獲得Enterprise bean的metadata信息。當(dāng)開發(fā)工具來編譯鏈接應(yīng)用程序的時候,或者配置工具來配置的時候,可能會用到metadata信息。Javax.ejb.EJBMetadata接口提供了獲得javax.ejb.EJBHome接口,home類,remote接口,還有獲得主鍵的方法。也提供了一個isSesson()的方法來確定在放這個home接口的對象是會話bean還是實(shí)體bean。IsStatelessSession()方法指示這個會話bean是有狀態(tài)還是無狀態(tài)的。如下代碼顯示了javax.ejb.EJBMetadata接口的定義部分的代碼。
          Public javax.ejb; Public interface EJBMetaData{
          EJBHome getEJBHome();
          Class getHomeInterfaceClass();
          Class getRemoteInterfaceClasss();
          Class getPrimaryKeyClass();
          Boolean isSession();
          Boolean isStatelesssSession();
          }
          對每一個Create()方法,EJB規(guī)范定義了如下的命名約定。它的返回值是會話bean的remote接口的類型。方法的名字只能是Create()。對會話bean類中的每一個ejbCreate()方法都必須有一個Create()與之對應(yīng)。 對于每一個Create()方法的參數(shù)的類型和數(shù)量都必須與會話bean類中的ejbCreate()方法相對應(yīng)。方法必須拋出java.rmi.RemoteException例外。 方法必須拋出javax.rmi.CreateExeption例外。 Create()方法的參數(shù)是用來初始化新的會話bean對象的。 如下代碼顯示了一個會話bean對象的不同的Create()方法,其中必須的部分用粗體顯示:
          public interface AtmHome extends javax.ejb.EJBHome{
          Atm create() throws java.rmi.RemoteException,javax.ejb.CreateException;
          Atm create(Profile preferredProfile)
          Throws java.rmi.RemoteExeption,javax.ehrows java.rmi.RemoteException,RemoveException;
          EJBMetaData getEJBMetaData() throws RemoteException;
          Homehandle getHomeHandle() throws RemoteException;
          }
          這里提供了兩個remove()方法來刪除Enterprise bean的實(shí)例。第一個remove方法是通過句柄來刪除一個Enterprise bean的實(shí)例。第二個remove方法通過主鍵來刪除一個Enterprise bean的實(shí)例。在眾多的Enterprise bean實(shí)例中,句柄唯一的標(biāo)識一個實(shí)例。一個句柄與它引用的Enterprise bean有相同的生命期??紤]一個實(shí)體對象,客戶可以通過一個句柄來重新獲得相應(yīng)的Enterprise bean的實(shí)例。一個句柄能夠?qū)?yīng)一個Enterprise bean對象的多個實(shí)例。例如,即使當(dāng)Enterprise bean對象所在的主機(jī)崩潰了,或者Enterprise bean對象在不同的機(jī)器之間移動,句柄仍是有效的。這里的句柄是Serialized句柄,與CORBA中的字符串化的CORBA對象的引用是相似的概念。
          在EJBHome接口中的第二個remove操作通過其主鍵來決定要刪除的Enterprise bean。主鍵可以是擴(kuò)展了Java Object類的任何類型,但是,必須要實(shí)現(xiàn)Java的Serializable接口。主鍵是標(biāo)識實(shí)體bean的主要的方法。通常,主鍵是數(shù)據(jù)庫中的一個關(guān)鍵字,唯一的定義了由實(shí)體bean代表的數(shù)據(jù)。方法getEJBMetaData()返回了Enterprise bean對象的metadata接口。這個接口允許客戶獲得Enterprise bean的metadata信息。當(dāng)開發(fā)工具來編譯鏈接應(yīng)用程序的時候,或者配置工具來配置的時候,可能會用到metadata信息。Javax.ejb.EJBMetadata接口提供了獲得javax.ejb.EJBHome接口,home類,remote接口,還有獲得主鍵的方法。也提供了一個isSesson()的方法來確定在放這個home接口的對象是會話bean還是實(shí)體bean。IsStatelessSession()方法指示這個會話bean是有狀態(tài)還是無狀態(tài)的。如下代碼顯示了javax.ejb.EJBMetadata接口的定義部分的代碼。
          Public javax.ejb;
          Public interface EJBMetaData{
          EJBHome getEJBHome();
          Class getHomeInterfaceClass();
          Class getRemoteInterfaceClasss();
          Class getPrimaryKeyClass();
          Boolean isSession();
          Boolean isStatelesssSession();
          }
          五、EJB的編程環(huán)境:
          1、 使用Jbuilder
          Jbuilder與EJB Container能夠進(jìn)行無縫連接。Jbuilder和Inprise的應(yīng)用服務(wù)器包括了所有的開發(fā)和配置Enterprise Beans的工具以及所需要的庫:運(yùn)行和管理Enterprise Bean的容器、命名服務(wù)、 事務(wù)服務(wù)、Java數(shù)據(jù)庫、開發(fā)Enterprise Beans所需要的API、一個增強(qiáng)的java-to-iiop編譯器,支持值類型和RMI信號等等。
          Jbuilder還提供了一個快速開發(fā)應(yīng)用程序Enterprise Beans的工具和向?qū)?。通過簡單而且直觀的步驟,向?qū)椭憬⒁粋€Enterprise Bean。自己設(shè)定某些缺省值,產(chǎn)生了bean的模板,在上面,我們可以增加我們自己的應(yīng)用邏輯。Jbuilder也提供了一個EJB的接口生成向?qū)АO驅(qū)г贓nterprise Bean的公共方法基礎(chǔ)上生成了Remote接口和Home接口。Jbuilder還提供一個配置器的向?qū)椭覀冎鸩降慕ML描述器文件。并將生成的Stubs集中到一個jar文件中。
          2、使用Jbuilder之外的集成環(huán)境:
          如果你使用其它的除了別的集成環(huán)境(IDE)。要確定使用了集成環(huán)境IDE所帶的容器工具。也要驗(yàn)證IDE是否支持EJB規(guī)范的相應(yīng)的版本,還要確定它是否正確的支持EJB的API。
          要確定JD到所支持的EJB容器的版本??梢酝ㄟ^檢查Inprise的安裝說明來確定EJB容器所支持的支持JDK的版本。
          在配置Enterprise Bean的時候,你必須使用Inprise的應(yīng)用服務(wù)器所提供的工具。這些工具能夠編輯和修改第三方的代理商提供的Inprise配置描述器。還能夠驗(yàn)證配置描述器,能夠驗(yàn)證bean的源代碼。
          六、一個簡單的HELLO例子
          1、安裝Apusic Application Server
          Note:以下以Linux為例,來說明Apusic Application Server的安裝過程。其他平臺的安裝,可參考Apusic Application Server安裝手冊。
          下載JDK1.2,Apusic Application Server必須運(yùn)行在JDK1.2以上環(huán)境中。可從以下站點(diǎn)下載最新JDK。
          http://java.sun.com
          下載Apusic Application Server
          Apusic Application Server 試用版可從以下網(wǎng)址得到:
          http://www.apusic.com/download/enter.jsp
          在下載完成后,你可以得到一個包裹文件apusic.zip,選定安裝目錄,假設(shè)安裝到/usr下,則用以下命令:
             cd /usr
          jar xvf apusic.zip
          /usr下會出現(xiàn)一個目錄apusic,Apusic Application Server的所有程序都被解壓到/usr/apusic下。
          將以下路徑加入到CLASSPATH中
          /usr/apusic/lib/apusic.jar
          $JAVA_HOME/lib/tools.jar
          用以下命令運(yùn)行Apusic Application Server
          java -Xms64m com.apusic.server.Main -root /usr/apusic
          2、定義EJB遠(yuǎn)程接口(Remote Interface)
          任何一個EJB都是通過Remote Interface被調(diào)用,EJB開發(fā)者首先要在Remote Interface中定義這個EJB可以被外界調(diào)用的所有方法。執(zhí)行Remote Interface的類由EJB生成工具生成。
          以下是HelloBean的Remote Inteface程序:
          package ejb.hello;
          import java.rmi.RemoteException;
          import java.rmi.Remote;
          import javax.ejb.*;
          public interface Hello extends EJBObject, Remote {
          file://this method just get "Hello World" from HelloBean.
          public String getHello() throws RemoteException;
          }
          3、定義Home Interface
          EJB容器通過EJB的Home Interface來創(chuàng)建EJB實(shí)例,和Remote Interface一樣,執(zhí)行Home Interface的類由EJB生成工具生成。
          以下是HelloBean 的Home Interface程序:
          package ejb.hello;
          import javax.ejb.*;
          import java.rmi.Remote;
          import java.rmi.RemoteException;
          import java.util.*;
          /**
          * This interface is extremely simple it declares only
          * one create method.
          */
          public interface HelloHome extends EJBHome {
          public Hello create() throws CreateException,
          RemoteException;
          }
          4、寫EJB類
          在EJB類中,編程者必須給出在Remote Interface中定義的遠(yuǎn)程方法的具體實(shí)現(xiàn)。EJB類中還包括一些 EJB規(guī)范中定義的必須實(shí)現(xiàn)的方法,這些方法都有比較統(tǒng)一的實(shí)現(xiàn)模版,編程者只需花費(fèi)精力在具體業(yè)務(wù)方法的實(shí)現(xiàn)上。
          以下是HelloBean的代碼:
          package ejb.hello;
          import javax.ejb.*;
          import java.util.*;
          import java.rmi.*;
          public class HelloBean implements SessionBean {
          static final boolean verbose = true;
          private transient SessionContext ctx;
          // Implement the methods in the SessionBean
          // interface
          public void ejbActivate() {
          if (verbose)
          System.out.println("ejbActivate called");
          }
          public void ejbRemove() {
          if (verbose)
          System.out.println("ejbRemove called");
          }
          public void ejbPassivate() {
          if (verbose)
          System.out.println("ejbPassivate called");
          }
          /**
          * Sets the session context.
          *
          * @param SessionContext
          */
          public void setSessionContext(SessionContext ctx) {
          if (verbose)
          System.out.println("setSessionContext called");
          this.ctx = ctx;
          }
          /**
          * This method corresponds to the create method in
          * the home interface HelloHome.java.
          * The parameter sets of the two methods are
          * identical. When the client calls
          * HelloHome.create(), the container allocates an
          * instance of the EJBean and calls ejbCreate().
          */
          public void ejbCreate () {
          if (verbose)
          System.out.println("ejbCreate called");
          }
          /**
          * **** HERE IS THE BUSINESS LOGIC *****
          * the getHello just return a "Hello World" string.
          */
          public String getHello()
          throws RemoteException
          {
          return("Hello World");
          }
          }
          5、創(chuàng)建ejb-jar.xml文件
          ejb-jar.xml文件是EJB的部署描述文件,包含EJB的各種配置信息,如是有狀態(tài)Bean(Stateful Bean) 還是無狀態(tài)Bean(Stateless Bean),交易類型等。ejb-jar.xml文件的詳細(xì)信息請參閱EJB規(guī)范。以下是HelloBean的配置文件:
          <?xml version="1.0"?>
          <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.2//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_2.dtd">
          <ejb-jar>
          <enterprise-beans>
          <session>
          <ejb-name>Hello</ejb-name>
          <home>ejb.hello.HelloHome</home>
          <remote>ejb.hello.Hello</remote>
          <ejb-class>ejb.hello.HelloBean</ejb-class>
          <session-type>Stateless</session-type>
          <transaction-type>Container</transaction-type>
          </session>
          </enterprise-beans>
          <assembly-descriptor>
          <container-transaction>
          <method>
          <ejb-name>Hello</ejb-name>
          <method-name>*</method-name>
          </method>
          <trans-attribute>Required</trans-attribute>
          </container-transaction>
          </assembly-descriptor>
          </ejb-jar>
            6、編譯和部署
            編譯Java源文件并將編譯后class和ejb-jar.xml打包到Hello.jar
            mkdir build
            mkdir build/META-INF
          cp ejb-jar.xml build/META-INF
            javac -d build *.java
            cd build
          jar cvf Hello.jar META-INF ejb
            cd ..
          用EJB工具生成可部署到Apusic Application Server中運(yùn)行的jar文件:
          java com.apusic.ejb.utils.EJBGen -d /usr/apusic/classes/Hello.jar build/Hello.jar
          增加/usr/apusic/classes/Hello.jar到CLASSPATH中
          將Hello.jar加入到Apusic Application Server配置文件中。在/usr/apusic/config/server.xml 加入以下幾行:
          <module>
          <ejb>
          <ejb-uri>classes/Hello.jar</ejb-uri>
          <bean>
          <ejb-name>Hello</ejb-name>
          <jndi-name>HelloHome</jndi-name>
          </bean>
          </ejb>
          </module>
          啟動服務(wù)器
          java -Xms64m com.apusic.server.Main -root /usr/apusic
          7、寫客戶端調(diào)用程序
          您可以從Java Client,JSP,Servlet或別的EJB調(diào)用HelloBean。
          調(diào)用EJB有以下幾個步驟:
          通過JNDI(Java Naming Directory Interface)得到EJB Home Interface
          通過EJB Home Interface 創(chuàng)建EJB對象,并得到其Remote Interface
          通過Remote Interface調(diào)用EJB方法
          以下是一個從Java Client中調(diào)用HelloBean的例子:
          package ejb.hello;
          import javax.naming.Context;
          import javax.naming.InitialContext;
          import java.util.Hashtable;
          import javax.ejb.*;
          import java.rmi.RemoteException;

          /**
          * @author Copyright (c) 2000 by Apusic, Inc. All Rights Reserved.
          */
          public class HelloClient{
          public static void main(String args[]){
          String url = "rmi://localhost:6888";
          Context initCtx = null;
          HelloHome hellohome = null;
          try{
          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_CONTEXT_FACTORY,
          "com.apusic.jndi.InitialContextFactory");
          env.put(Context.PROVIDER_URL, url);
          initCtx = new InitialContext(env);
          }catch(Exception e){
          System.out.println("Cannot get initial context: " + e.getMessage());
          System.exit(1);
          }
          try{
          hellohome = (HelloHome)initCtx.lookup("HelloHome");
          Hello hello = hellohome.create();
          String s = hello.getHello();
          System.out.println(s);
          }catch(Exception e){
          System.out.println(e.getMessage());
          System.exit(1);
          }
          }
          }
          運(yùn)行HelloClient,可得到以下輸出:
          Hello World

          http://yingmingpan.bokee.com/tb.b?diaryId=14817948

          posted @ 2007-09-30 10:20 夏雪 閱讀(379) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 凉山| 禄丰县| 米脂县| 台湾省| 绥宁县| 普定县| 合阳县| 雅安市| 兴宁市| 民县| 修水县| 白城市| 虎林市| 古交市| 辽源市| 开远市| 防城港市| 洞头县| 深泽县| 湖南省| 阿克苏市| 得荣县| 苍南县| 巨野县| 土默特左旗| 清水县| 太湖县| 那坡县| 读书| 房山区| 扎鲁特旗| 温州市| 淮阳县| 武汉市| 永修县| 义乌市| 涿鹿县| 桃园市| 宜章县| 万源市| 桐城市|