從JSP調用EJB
本專題我們討論用JSP,Servlets,和JavaBean來構成一個系統的幾種途徑。下面有集中不同的結構。每一種都是從上一種發展過來的,下面的圖表顯示了發展過程的梗概。
當Sun公司開始推廣JSP的時候,很多人認為可以通過Web頁面來請求的企業級結構將會取代Servlet。雖然JSP是J2EE規范的關鍵組成部分,它處理請求和應答機制。我們需要更深入的研究JSP和Servlet的關系。
專題的另一個部分談了JSP代碼的實現細節,如何編譯轉化為Servlet。JSP是建立在Servlet API基礎之上的,;利用了Servlet的語義。理解了這個,提出一個有意思的問題:我們是否不再需要在使用WEB的系統中開發標準的Servlet?是否有一種方法來結合JSP和Servlet?如果有的話,我們需要把Java代碼放在何處?在JSP的請求過程中,是否涉及到了其他的組件,比如說JavaBean?如果有的話,他們在這個體系結構處與什么位置,扮演什么角色?
明白這些問題是很重要的。雖然JSP技術是基本的Servlet技術的繼承,但是JSP在Servlet的基礎上有所創新,他們可以互相協作,互相補充對方的不足。
在這個前提下,我們來探討這兩種Java擴充技術的和其它技術,比如JavaBean,的協作問題。在討論結構之前,我們先簡單的看看開發這樣的結構的系統有什么要求。
1、 代碼因素、角色分離:
JSP技術能夠進展到現在這個地步,一個很重要的原因就是需要有一門技術,通過把靜態數據變成分離的動態數據,來簡化應用程序的設計。JSP是把java代碼嵌入HTML頁面中。但是,隨著應用程序越來越多的基于應用邏輯對象和多層體系結構,需要把java代碼從HTML頁面中分離出來,同時還需要保持JSP技術的完整性和靈活性。好的應用程序設計風格要求把應用對象分離出來,應用對象的聲明和操作也要易于區分。
使用JSP的另一個好處是讓我們更明確的把WEB頁面的設計和軟件的設計區分開來。注意,通常的Servlet都是以JAVA代碼的形式嵌入到HTML頁面中。現在,把Servlet看成是java代碼的獨立的容器,而的HTML頁面變成了JSP頁面。新的問題又出現了,需要把多少JAVA代碼放在JSP中,把JAVA代碼從JSP中分離出來,我們把它放在什么地方。
對于所有基于Web的工程,都存在不同角色,不同責任的情況。例如:有人負責編寫HTML頁面,還有人負責java程序設計。
對于那些規模比較小的工程,也許這些都是由一個或者兩個聯系非常緊密的人人來完成。但是對于稍微大一點的工程,更傾向于把把任務分給多人來完成,也許這寫HTML的人不懂JAVA,寫JAVA的對HTML卻不很熟悉。這樣,如果太依賴于別人的工作進度,工作效率就會很低。
如果能夠把JAVA代碼從JSP中分離出來,那么,軟件開發人員和WEB設計人員的工作就相對來說更獨立了一些。但是,這樣的情況可能會導致容易發生一些錯誤,因為兩部分人獨立之后,每部分人都可能不經意就改變了自己的代碼。
這樣我們就理解了為什么我們會繼續使用一些基本的Servlet。它是一個裝載從JSP中分離出來的JAVA代碼的容器,是軟件開發人員與JSP頁面盡量保持一種松散的聯系。當然,還需要有人來寫JSP代碼頁面。但是其依賴性就降低了。
為了支持這樣的開發人員角色的分離,要使JSP中的JAVA代碼盡量的少。我們知道,有些JAVA代碼是以Servlet的形式起中間媒介作用的。象身份鑒定,是一種多次請求的代碼,這樣就很適合用Servlet來實現。
同時,要把許多的應用邏輯和數據訪問代碼從JSP中分離出來,包裝成為JavaBean。這樣,我們可以看到從JSP中的JAVA代碼轉移到了兩個地方,一個是作為Servlet,代碼在JSP的前端,還有一個就是作為后端的JavaBean。
Redirecting and Forwording
Redirect和Forword是JSP和Servlet經常使用的兩種機制。
當JSP或者Servlet用Redirect機制來重定向客戶的請求的時候,服務器給客戶發一個http 302消息,告訴客戶要請求的資源轉移到了另一個URL。然后客戶再去向新的URL發請求。而Forword機制是指Servlet發現要請求的資源在另一個URL上,那么,由Servlet自己去象這個URL請求,然后再把結果送給客戶。
體系結構:
在討論體系結構之前,有必要提一下兩種使用JSP技術的基本方法
第一種是以頁面為中心(Page-Centric)的(Client/Server)方式,這樣方式是把請求直接發給JSP頁面。
第二中方式分發方式(多層結構的)。這種方式以一個JSP或者Servlet做為一個基本的控制器,又它把請求分給JSP頁面或者JavaBean。
下來,我們會用一個簡單的例子來討論這兩種方式的區別。
1、首先我們看看Page-Centric的結構。這樣的結構通常都是來客戶機和服務器端都有一個應用程序。最常見的例子是PowerBuilder的例子。Servlet或者JSP在服務器端截獲客戶的請求,直接的訪問數據庫等資源。
這個結構的優點就是編程簡單。允許頁面的設計者根據資源的狀態動態的生成頁面的內容。但是,這樣的結構不適合多個客戶同時訪問資源,這樣同時會有大量的請求需要服務端來處理。每個請求都會建立一個連接,消耗一定的資源。這樣,需要讓這些連接共享一些資源。其中最明顯的例子就是用JDBC連接數據庫中用到的連接池(Connection pools)。對結構使用不適當的話,會導致JSP中出現大量的JAVA代碼。這雖然對java程序設計人員不會有什么問題,可是大量的代碼分散在JSP中,不利于維護和修改。
這種基本的結構包括通過嵌入在JSP中的代碼直接的向服務器發請求。這樣有幾個優點:從開發看來,成本很低。所有的JAVA代碼都嵌入到HTML中,復雜性降低了。
減少了復雜性,當系統規模增大的時候,這種結構的缺點也出來了。例如,把太多的應用邏輯放到了JSP頁面中。前面我們提到,利用Servlet或者Bean可以把開發者角色分的更清楚,提高代碼的可重用性。
下面我們主要多層結構的JSP,在這種結構下,用一個Servlet或者JSP當作主要的控制器。將所有的客戶請求分配給其它的JSP、JavaBean或者EJB。
在一個多層結構的應用系統中,服務端的被分成好幾層,如下圖所示:
在這個例子中,應用系統是一個多層結構的,中間層的JSP通過其它的對象或者Enterprise JavaBean來訪問后臺資源。EJB服務器和EJB提供訪問訪問資源的方法,支持事務,支持安全管理。目前,J2EE支持這樣的編程。
多層結構應用設計的第一步,是明確系統涉及的對象和他們之間的關系。也就是首先要確定對象模式。這一步可以通過Rational Rose之類的工具來實現。通常,對象模式的確定需要特別小心,如果對象太多,會增加系統的復雜性。如果對象模式做的好的話,可以說系統就成功了一半。
然后是確定JSP和Servlet,根據他們扮演的角色的不同,分成兩類。在J2EE中都叫做web組件。
1、 一類(Front-end)是用來處理應用流程的。其中有很多對后臺對象(例如EJB)的調用。他們不處理頁面的表示問題,只是截獲從用戶過來的請求,提供應用系統的一個入口。簡化安全管理,而且易于控制應用系統的狀態。
2、 還有一類(Presentation)是用來生成HTML或者XML的。這些JSP主要的目的就是來動態的產生頁面內容。
下圖表示了這兩類之間的關系,第一類(Front-end)接收一個請求,然后選擇一個合適的Presentation組件來處理。處理之后,或者直接把結果返回給客戶,或者把結果返回給另一個前端組件。
有時這種對象模式的劃分是比較困難的。但是,劃分的越清晰,那設計也越清晰。
這種分類有點類似與模式-視圖(Model-View)結構,front-end相當于Model,presentation相當與View。如下圖:
在這個模式下,JSP用來產生表示層或者執行一些操作。Front-end組件當作控制器,處理客戶的請求,生成JavaBean和EJB對象。而在Presentation JSP組件中并沒有處理邏輯過程,只是簡單的從別的對象中得到數據,然后動態的插入到模板中。
如果不愿意寫front-end類型的Servlet,也可以寫一些只包括代碼,不包含任何Presentation表示部分的邏輯。不管是寫Servlet還是寫JSP,這樣的結構都把內容和表示分開了,將程序開發者和頁面設計者比較明確的區分開來。對系統,特別是比較大規模的系統的設計是比較適合的。
在JSP中使用JavaBean和用Enterprise JavaBean是有區別的。使用JavaBean的時候,通常只是使用簡單的類來包裝數據信息。JavaBean通過簡單的get或者set方法來存取JavaBean的屬性值。允許用jsp:usebean標簽來動態的設定屬性值。用Enterprise JavaBean的話,過程比較復雜一些,首先要初始化上下文,然后通過名字根據名字服務找到所需要的EJB,創建一個實例之后,就可以使用了。
Enterprise JavaBean的寫法和配置我們原來談過,這里就不再說了,感興趣的朋友可以查閱以前的文章,這里我們只是舉一個如何從JSP中調用EJB的例子。例子的環境是tomcat3.1+IAS4.1。寫好EJB之后,首先要啟動IAS,并且在IAS中配置好EJB。然后在IAS的安裝目錄下的ejb_jar子目錄下面,你會發現有jar文件,以后每次IAS啟動之后,就把這個目錄下所有的jar文件中的EJB都裝載好,并把JNDI名字注冊到JNDI的名字空間中。以后就可以直接通過JNDI名字獲得EJB的接口了。
假設我們已經寫好了一個EJB。也已經啟動了IAS,并配置好。其JNDI名字為“Sample”。要從JSP中調用EJB,需要先把javax.naming.* 、javax.rmi.PortableRemoteObject import進來,另外,還要把EJB組件的home接口,remote接口也import進來:
〈%@ page import = "javax.naming.*"%> //JNDI名字服務模塊
<%@ page import = "javax.rmi.PortableRemoteObject"%> //RMI調用模塊
<%@ page import = "samples.*"%> //EJB的home,remote接口
<%
InitialContext ctx = new InitialContext(); //初始化上下文
Object ref = ctx.lookup("Sample"); //通過JNDI名字得到對象的接口
SampleHome samplehome;
samplehome=(SampleHome)PortableRemoteObject.narrow(ref,SampleHome.class);
//把對象強制轉化為home接口類。這樣就得到了EJB對象的Home接口,然
//后再按EJB規范,根據不同的EJB類型,通過不同的方法得到EJB的
//Home接口類。
SampleRemote remote = samplehome.create();
//通過home接口的create方法得到remote接口。
//接下來就可以通過remote來調用EJB中的方法了。
……
……
%>



當Sun公司開始推廣JSP的時候,很多人認為可以通過Web頁面來請求的企業級結構將會取代Servlet。雖然JSP是J2EE規范的關鍵組成部分,它處理請求和應答機制。我們需要更深入的研究JSP和Servlet的關系。
專題的另一個部分談了JSP代碼的實現細節,如何編譯轉化為Servlet。JSP是建立在Servlet API基礎之上的,;利用了Servlet的語義。理解了這個,提出一個有意思的問題:我們是否不再需要在使用WEB的系統中開發標準的Servlet?是否有一種方法來結合JSP和Servlet?如果有的話,我們需要把Java代碼放在何處?在JSP的請求過程中,是否涉及到了其他的組件,比如說JavaBean?如果有的話,他們在這個體系結構處與什么位置,扮演什么角色?
明白這些問題是很重要的。雖然JSP技術是基本的Servlet技術的繼承,但是JSP在Servlet的基礎上有所創新,他們可以互相協作,互相補充對方的不足。
在這個前提下,我們來探討這兩種Java擴充技術的和其它技術,比如JavaBean,的協作問題。在討論結構之前,我們先簡單的看看開發這樣的結構的系統有什么要求。
1、 代碼因素、角色分離:
JSP技術能夠進展到現在這個地步,一個很重要的原因就是需要有一門技術,通過把靜態數據變成分離的動態數據,來簡化應用程序的設計。JSP是把java代碼嵌入HTML頁面中。但是,隨著應用程序越來越多的基于應用邏輯對象和多層體系結構,需要把java代碼從HTML頁面中分離出來,同時還需要保持JSP技術的完整性和靈活性。好的應用程序設計風格要求把應用對象分離出來,應用對象的聲明和操作也要易于區分。
使用JSP的另一個好處是讓我們更明確的把WEB頁面的設計和軟件的設計區分開來。注意,通常的Servlet都是以JAVA代碼的形式嵌入到HTML頁面中。現在,把Servlet看成是java代碼的獨立的容器,而的HTML頁面變成了JSP頁面。新的問題又出現了,需要把多少JAVA代碼放在JSP中,把JAVA代碼從JSP中分離出來,我們把它放在什么地方。
對于所有基于Web的工程,都存在不同角色,不同責任的情況。例如:有人負責編寫HTML頁面,還有人負責java程序設計。
對于那些規模比較小的工程,也許這些都是由一個或者兩個聯系非常緊密的人人來完成。但是對于稍微大一點的工程,更傾向于把把任務分給多人來完成,也許這寫HTML的人不懂JAVA,寫JAVA的對HTML卻不很熟悉。這樣,如果太依賴于別人的工作進度,工作效率就會很低。
如果能夠把JAVA代碼從JSP中分離出來,那么,軟件開發人員和WEB設計人員的工作就相對來說更獨立了一些。但是,這樣的情況可能會導致容易發生一些錯誤,因為兩部分人獨立之后,每部分人都可能不經意就改變了自己的代碼。
這樣我們就理解了為什么我們會繼續使用一些基本的Servlet。它是一個裝載從JSP中分離出來的JAVA代碼的容器,是軟件開發人員與JSP頁面盡量保持一種松散的聯系。當然,還需要有人來寫JSP代碼頁面。但是其依賴性就降低了。
為了支持這樣的開發人員角色的分離,要使JSP中的JAVA代碼盡量的少。我們知道,有些JAVA代碼是以Servlet的形式起中間媒介作用的。象身份鑒定,是一種多次請求的代碼,這樣就很適合用Servlet來實現。
同時,要把許多的應用邏輯和數據訪問代碼從JSP中分離出來,包裝成為JavaBean。這樣,我們可以看到從JSP中的JAVA代碼轉移到了兩個地方,一個是作為Servlet,代碼在JSP的前端,還有一個就是作為后端的JavaBean。
Redirecting and Forwording
Redirect和Forword是JSP和Servlet經常使用的兩種機制。
當JSP或者Servlet用Redirect機制來重定向客戶的請求的時候,服務器給客戶發一個http 302消息,告訴客戶要請求的資源轉移到了另一個URL。然后客戶再去向新的URL發請求。而Forword機制是指Servlet發現要請求的資源在另一個URL上,那么,由Servlet自己去象這個URL請求,然后再把結果送給客戶。
體系結構:
在討論體系結構之前,有必要提一下兩種使用JSP技術的基本方法
第一種是以頁面為中心(Page-Centric)的(Client/Server)方式,這樣方式是把請求直接發給JSP頁面。
第二中方式分發方式(多層結構的)。這種方式以一個JSP或者Servlet做為一個基本的控制器,又它把請求分給JSP頁面或者JavaBean。
下來,我們會用一個簡單的例子來討論這兩種方式的區別。
1、首先我們看看Page-Centric的結構。這樣的結構通常都是來客戶機和服務器端都有一個應用程序。最常見的例子是PowerBuilder的例子。Servlet或者JSP在服務器端截獲客戶的請求,直接的訪問數據庫等資源。

這個結構的優點就是編程簡單。允許頁面的設計者根據資源的狀態動態的生成頁面的內容。但是,這樣的結構不適合多個客戶同時訪問資源,這樣同時會有大量的請求需要服務端來處理。每個請求都會建立一個連接,消耗一定的資源。這樣,需要讓這些連接共享一些資源。其中最明顯的例子就是用JDBC連接數據庫中用到的連接池(Connection pools)。對結構使用不適當的話,會導致JSP中出現大量的JAVA代碼。這雖然對java程序設計人員不會有什么問題,可是大量的代碼分散在JSP中,不利于維護和修改。
這種基本的結構包括通過嵌入在JSP中的代碼直接的向服務器發請求。這樣有幾個優點:從開發看來,成本很低。所有的JAVA代碼都嵌入到HTML中,復雜性降低了。

減少了復雜性,當系統規模增大的時候,這種結構的缺點也出來了。例如,把太多的應用邏輯放到了JSP頁面中。前面我們提到,利用Servlet或者Bean可以把開發者角色分的更清楚,提高代碼的可重用性。
下面我們主要多層結構的JSP,在這種結構下,用一個Servlet或者JSP當作主要的控制器。將所有的客戶請求分配給其它的JSP、JavaBean或者EJB。
在一個多層結構的應用系統中,服務端的被分成好幾層,如下圖所示:

在這個例子中,應用系統是一個多層結構的,中間層的JSP通過其它的對象或者Enterprise JavaBean來訪問后臺資源。EJB服務器和EJB提供訪問訪問資源的方法,支持事務,支持安全管理。目前,J2EE支持這樣的編程。
多層結構應用設計的第一步,是明確系統涉及的對象和他們之間的關系。也就是首先要確定對象模式。這一步可以通過Rational Rose之類的工具來實現。通常,對象模式的確定需要特別小心,如果對象太多,會增加系統的復雜性。如果對象模式做的好的話,可以說系統就成功了一半。
然后是確定JSP和Servlet,根據他們扮演的角色的不同,分成兩類。在J2EE中都叫做web組件。
1、 一類(Front-end)是用來處理應用流程的。其中有很多對后臺對象(例如EJB)的調用。他們不處理頁面的表示問題,只是截獲從用戶過來的請求,提供應用系統的一個入口。簡化安全管理,而且易于控制應用系統的狀態。
2、 還有一類(Presentation)是用來生成HTML或者XML的。這些JSP主要的目的就是來動態的產生頁面內容。
下圖表示了這兩類之間的關系,第一類(Front-end)接收一個請求,然后選擇一個合適的Presentation組件來處理。處理之后,或者直接把結果返回給客戶,或者把結果返回給另一個前端組件。

有時這種對象模式的劃分是比較困難的。但是,劃分的越清晰,那設計也越清晰。
這種分類有點類似與模式-視圖(Model-View)結構,front-end相當于Model,presentation相當與View。如下圖:

在這個模式下,JSP用來產生表示層或者執行一些操作。Front-end組件當作控制器,處理客戶的請求,生成JavaBean和EJB對象。而在Presentation JSP組件中并沒有處理邏輯過程,只是簡單的從別的對象中得到數據,然后動態的插入到模板中。
如果不愿意寫front-end類型的Servlet,也可以寫一些只包括代碼,不包含任何Presentation表示部分的邏輯。不管是寫Servlet還是寫JSP,這樣的結構都把內容和表示分開了,將程序開發者和頁面設計者比較明確的區分開來。對系統,特別是比較大規模的系統的設計是比較適合的。
在JSP中使用JavaBean和用Enterprise JavaBean是有區別的。使用JavaBean的時候,通常只是使用簡單的類來包裝數據信息。JavaBean通過簡單的get或者set方法來存取JavaBean的屬性值。允許用jsp:usebean標簽來動態的設定屬性值。用Enterprise JavaBean的話,過程比較復雜一些,首先要初始化上下文,然后通過名字根據名字服務找到所需要的EJB,創建一個實例之后,就可以使用了。
Enterprise JavaBean的寫法和配置我們原來談過,這里就不再說了,感興趣的朋友可以查閱以前的文章,這里我們只是舉一個如何從JSP中調用EJB的例子。例子的環境是tomcat3.1+IAS4.1。寫好EJB之后,首先要啟動IAS,并且在IAS中配置好EJB。然后在IAS的安裝目錄下的ejb_jar子目錄下面,你會發現有jar文件,以后每次IAS啟動之后,就把這個目錄下所有的jar文件中的EJB都裝載好,并把JNDI名字注冊到JNDI的名字空間中。以后就可以直接通過JNDI名字獲得EJB的接口了。
假設我們已經寫好了一個EJB。也已經啟動了IAS,并配置好。其JNDI名字為“Sample”。要從JSP中調用EJB,需要先把javax.naming.* 、javax.rmi.PortableRemoteObject import進來,另外,還要把EJB組件的home接口,remote接口也import進來:
〈%@ page import = "javax.naming.*"%> //JNDI名字服務模塊
<%@ page import = "javax.rmi.PortableRemoteObject"%> //RMI調用模塊
<%@ page import = "samples.*"%> //EJB的home,remote接口
<%
InitialContext ctx = new InitialContext(); //初始化上下文
Object ref = ctx.lookup("Sample"); //通過JNDI名字得到對象的接口
SampleHome samplehome;
samplehome=(SampleHome)PortableRemoteObject.narrow(ref,SampleHome.class);
//把對象強制轉化為home接口類。這樣就得到了EJB對象的Home接口,然
//后再按EJB規范,根據不同的EJB類型,通過不同的方法得到EJB的
//Home接口類。
SampleRemote remote = samplehome.create();
//通過home接口的create方法得到remote接口。
//接下來就可以通過remote來調用EJB中的方法了。
……
……
%>