action="#{manager.sayHello}"/>
通過以下的實體表單,JSF頁面顯示了資料庫中所有已經向Seam說“hello”的用戶。用戶名單列表存儲在一個名為“ fans”的Seam組件中,它是一個 List 對象。JSF dataTable通過遍歷列表,每一行顯示一個 Person對象。 Fan標記是 fans列表的迭代子。
當用戶點擊“Say Hello”按鈕提交表單,Seam用輸入數據構造了該person組件。然後它調用了名為“ manager”的Seam 組件的sayhello()的方法(像這樣, #{manager.sayHello}是表單提交按鈕的UI事件處理器),這就在資料庫中保存了 person對象並且刷新了 fans列表。名為 manager的組件是一個EJB3的會話bean, 我們將在下節討論該話題。
Seam 中的名為manager的組件是會話bean Manager
Action,正如該類中 @Name註解指定的。 ManagerAction類有 person和 fans兩個屬性,這兩個屬性被 @In和 @Out所註解。
@Stateless
@Name("manager")
public class ManagerAction implements Manager {
@In @Out
private Person person;
@Out
private List
fans; 註解@In和@Out在Seam編程模型中處於核心。因此,讓我們看看到底它們在這裡是做什麼的。註解@In告訴Seam,在此會話bean中,執行任何一個方法之前,Seam就會把由JSF表單構造的名為person組件賦給該person欄位(通過依賴注入)。開發者能為@In中的注入的組件指定一個任意的名稱,但是如果沒有指定,如這裡所示,Seam會將同類型以及同名稱的組件注入到該欄位中。註解@Out告訴Seam,在執行任何方法后,Seam會將屬性fans值和屬性person的值都賦給被Seam管理的同名的組件。在Seam中,我們將這個操作稱作“依賴拋出”。以此,在ManagerAction.sayHello()方法中,我們僅僅需要更新屬性fans和屬性person的值,它們會自動顯示在頁面上。
什麼是雙向映射
在Seam 文件中,有時你就會看到術語“雙向映射”。它指的是被Seam管理的組件和Seam管理上下之間的注入和拋出。
因為 person屬性已經通過注入持有了表單數據, sayHello()方法僅僅是通過JPA EntityManager將它保存到資料庫中,JPA EntityManager也是通過 @PersistenceContext注入的。當方法返回之後,它便更新了 fans和 person對象並且把這兩個對象拋出。方法 sayHello()一般會返回null,預示著在調用之後,更新的數據模型將在當前的JSF頁面顯示。
@PersistenceContext
private EntityManager em;
public String sayHello () {
em.persist (person);
person = new Person ();
fans = em.
createQuery("select p from Person p")
.getResultList();
return null;
}除了一些細節,我們基本完成了。可能你已經注意到,ManagerAction bean類實現了Manager介面。為了符合EJB3會話bean 規範,我需要一個能列出
bean中所有業務方法的方法。下面是介面Manager代碼,幸運的是,用任何高級IDE工具都能輕鬆地自動生成這個介面。
@Local
public interface Manager {
public String sayHello ();
}這就是在Hello World例子中需要的所有代碼。後面兩章小節將涵蓋Seam應用的其他方法和配置。如果開發者為了自己的小型資料庫應用想立即編碼和定製
helloworld項目,那麼現在就可以跳過本章的剩餘部分。
現在我們已經大致了解了Hello World的應用。但是我們還有一些重要的話題繼續,例如其他折中途徑以及前面代碼沒有涉及到重要特性,我們將在本節討論這些話題。它們能幫助開發者對seam更深刻的理解,但是如果你沒有耐心,可以直接跳過本節,需要的時再來閱讀。
4.1 Seam POJO組件 上例中,我們用一個EJB3會話bean實現了應用邏輯,但是我們並不局限於EJB3組件。事實上,Seam中任何一個有 @Name註解的POJO都能被轉化為一個可管理的組件。
例如,我們能將 ManagerAction轉化為一個 POJO,而不是一個EJB3 session bean。
@Name("manager")
public class ManagerAction {
@In (create=true)
private EntityManager em;
... ...
} 使用POJO取代EJB3 bean有正反兩方面意見,使用POJO編程時很簡單,因為它們不需要EJB3特有的註解和介面(參見上文)。如果你的所有業務組件都是Seam POJO, 那麼你就能不依賴EJB3應用伺服器,運行你的Seam 應用(參見23章,沒有EJB3的Seam)。
但是,POJO比EJB3的功能少,因為POJO不能獲得EJB3容器服務。在不依賴EJB3的Seam 中喪失的EJB3服務就包括以下幾點:
@PersistenceContext注入在POJO中不在管用。為了在一個Seam POJO中得到EntityManager,開發者不得不在Seam配種文件中初始化EntityManager,然後使用Seam註解@In將它注入到POJO中。 POJOs中將不在支持方法級別事務聲明(declarative method-level transaction)。相反,你可以配置Seam來劃分事務,可以從收到web請求開始直到響應頁面產生結束。 Seam POJO不是消息驅動組件。不支持註解為 @Asynchronous的方法。不支持容器安全管理。沒有事務或者組件級別的持久上下文。Seam POJO中的所有的持久上下文都是經過拓展的(更多細節請參見7.1 “默認的對話作用域”)。沒有集成容器管理的體系結構(例如,JMX控制台服務)。 Seam POJO方法中沒有Java RMI。 Seam POJO不能是註解為 @WebService組件。沒有JCA集成。所以當在EJB3容器中進行部署時,為什麼每個人都想使用POJO組件?答案就是,POJO組件對於純“業務邏輯”組件非常有益。POJO為其他組件代理了數據訪問、消息傳遞和其他基本功能。例如,我們能使用POJO組件操縱Seam數據訪問對象,這對“業務邏輯”POJO是非常有用的,因為它們可以在需要的時候,在其他框架中被重用。但是總的來說,它們的應用要比EJB3組件少,特別是在中小型應用中。所以,本書的大多數例子我們都使用EJB3組件。
4.2 易於測試我們已經在第一章中提到,Seam為了不依賴容器的方便的測試,進行了重新設計。在 helloworld項目中,我們在測試文件夾中包括了單元測試和集成測試這兩個測試用例。在純Java SE環境下,Seam 測試體系模擬了資料庫、JSF、Seam上下文以及其他應用伺服器服務,只要運行ant test命令就能運行所有的測試。
4.3 基於Getter和Setter的雙向映射 在Hello World一例中,我們已經展示了通過成員變數對Seam組件進行的雙向映射,你也能通過Getter和Setter方法對組件進行雙向映射。例如,以下代碼就工作的很好。
private Person person;
@In
public void setPerson (Person person) {
this.person = person;
}
@Out
public Person getPerson () {
return person;
}
@Out
return fans;
}雖然以上的getter和setter方法看似輕微,利用getter和setter方法的雙向映射真正的價值在於能其加入定製邏輯來操縱雙向映射的過程。例如,你可以驗證被注入的對象或者快速地從資料庫重新得到被拋出的對象。
4.4避免過度的雙向映射在Hello World一例中 ,通過將數據組件作為業務組件的屬性,可以輕易的減少或者消除雙向映射。在JSF頁面中,通過這種方式,開發者只需要引用業務組件,而不需要在業務組件和數據組件之間的雙向映射。例如,開發者可以修改 ManagerAction類為以下所述。
依賴雙向映射是一個非常實用的設計模式。但是,正如其他設計模式,過度使用就會有害。過度的依賴雙向映射讓代碼變得難以閱讀,因為開發者必須理解每個注入的組件出自何處。過度的依賴雙向映射也能增加性能消耗,因為雙向映射是在運行時進行。
@Stateless
@Name("manager")
public class ManagerAction implements Manager {
private Person person;
public Person getPerson () {return person;}
public void setPerson (Person person) {
this.person = person;
}
public List
getFans () {return fans;}... ...
}接下來,我們在頁面上引用的屬性如下:
Please enter your name:
action="#{manager.sayHello}"/>
... ...
最後,具有了依賴管理的Seam是多用的。通常用數據訪問業務組件封裝數據是一項好的實踐,特別是針對有狀態業務組件。
4.5 JSF中的頁面導航本例中,只有一個頁面。每次點擊按鈕后,JSF頁面會重新顯示更新過的數據模型。顯然,大多數web應用多於一個頁面。在JSF中,一個用戶界面事件處理器能通過返回導航規則名稱,決定下一步該顯示哪個頁面。例如,開發者可以在 navigation.xml中定義以下導航規則。
anotherPage
/anotherPage.jsp
之後,如果sayHello()方法返回一個名為“another page”的字元串,JSF下一步就該展示anotherPage.jsp。UI事件處理器決定了接下來要顯示哪個頁面,從而為我們帶來了有步驟的控制。
4.6 通過EntityManager訪問資料庫JPA(Java Persistence API) EntityManager管理著關係資料庫表與實體bean 之間的映射。 EntityManager在運行時由應用伺服器創建。你能使用註解@PersistenceContext,注入一個EntityManager的實例。
EntityManager.persist()方法將實體bean存為與之對應數據表的一條記錄。EntityManager.query()方法運行SQL化的查詢,並以實體bean集合形式從資料庫返回數據。更多細節請參考JPA文件中關於如何使用EntityManager和查詢語言。在本書中,我們只用最簡單的查詢語句。
默認地,EntityManager將數據存於嵌入的HSQL資料庫中。如果在本機上運行Jboss AS,可以通過以下步驟,為HSQL資料庫開啟一個GUI控制台:訪問 http://localhost:8080/jmx-console/,點擊 database=localDB,service=Hypersonic MBean服務,之後,點擊在startDatabaseManager方法下方的“invoke”按鈕。你就可以從控制台執行任意SQL指令。
下面,我們將轉移話題,討論配置文件和應用程序打包。實際上,你可以通過Seam Gen命令行工具,生成幾乎所有的配置文檔和構造腳本文件。或者你也可以簡單的重用在示例中的源文件。所以,如果你想首先學習Seam編程技術,但又擔心接下來的配置和部署,這個是很正常的。你可以完全放心地跳過本節,需要的時候可以再次閱讀。
本節中,我們集中探討Seam EJB3組件配置,JBoss AS外的Seam POJO配置和部署當然是可行的。
大多數Seam配置文件都是XML文檔。但是等等!我們剛才不是承諾Seam能讓我們擺脫J2EE和Spring中的XML地獄嗎?為什麼它又有了XML文檔呢? 是的,XML文檔確實有很多用處。XML文檔非常適合部署階段的配置(例如web應用的根URL和後台資料庫的定位)。因為它允許我們在部署階段改變配置而不需要改變和重新編譯源代碼。它也適合粘合應用伺服器中的不同子系統(例如,配置如何讓JSF組件與Seam EJB3組件交互)。XML文檔也非常適合表示層相關內容(例如網頁和頁面導航流程)。
我們反對在XML文檔中重複已經存在於Java源代碼中的信息。開發者很快就會發現,這個簡單的SeamEJB3 應用有多個XML配置文檔,每個文檔那個都非常簡短,並且沒有一個包含存在於Java代碼中的信息。換句話說,Seam中沒有“XML代碼”。
進一步講,XML文檔中的大多數內容都是靜態的。所以開發者能在自己的Seam應用中輕鬆地重用這些文檔。如何使用示例作為自己的應用模板的介紹,請參見附錄B——使用應用示例作為模板。
我們將用下面幾頁來詳細講解示例應用的配置文檔和打包后的目錄結構。如果你沒有耐心看下去,而且很滿意這個應用模板,你可以跳過以下內容。不管怎樣,不再羅嗦, 我們一起來了解hello world示例是如何進行配置和打包的。為了構建一個JBoss AS的部署Seam 應用,我們必須將以上所有java 類和配置文檔打包為企業應用程序歸檔(EAR)。該例中,EAR文件是 helloworld.ear。它包含了三個JAR文件那個和兩個XML配置文檔。
helloworld.ear
|+ app.war //包含Web頁面等
|+ app.jar //包含Seam組件
|+ jboss-seam.jar // Seam庫
|+ META-INF
|+ application.xml
|+ jboss-app.xml 源代碼目錄
在此項目的源代碼中,resources/WEB-INF目錄包含屬於app.war/WEB-INF目錄的配置文檔。resources/META-INF目錄包含屬於app.jar/META-INF和helloworld.ear/META-INF的文檔。Resources根目錄包含屬於根目錄app.jar的文檔。
application.xml文檔列出了在EAR中的JAR文件,並為該應用指定了根URL。
Seam Hello World
app.war
/helloworld
app.jar
jboss-seam.jar
jboss-app.xml文檔為該應用指定了類載入器,每個EAR應用的類載入器應該有一個唯一的名稱。這裡我們使用應用程序名作為類載入器的名稱,以避免重複。
helloworld:archive=helloworld.ear
jboss-seam.jar是Seam發布Seam類庫。app.war和app.jar文檔由我們來建構。所以,下面我們要研究app.war和app.jar。
5.1. WAR文件 app.war是按照Web應用程序歸檔規範打包的JAR文件,它包含頁面和標準的JSF/Seam配置文檔。你還可以將JSF特有的類庫文件放入 WEB-INF/lib目錄 (例如 jboss-seam-ui.jar)。
app.war
|+ hello.jsp
|+ index.html
|+ WEB-INF
|+ web.xml
|+ faces-config.xml
|+ components.xml
|+ navigation.xml web.xml文檔是所有java EE web應用必需的。JSF用它來配置JSF servlet控制器,Seam用它來攔截所有的web請求。該配置文檔的相當標準。
xmlns="3"
xmlns:xsi="..."
xsi:schemaLocation="...">
org.jboss.seam.servlet.SeamListener
org.apache.myfaces.webapp.StartupServletContextListener
javax.faces.STATE_SAVING_METHOD
Faces Servlet
javax.faces.webapp.FacesServlet
1
Faces Servlet
*.seam
faces-config.xml文檔是JSF標準的配置文檔,Seam用它來將其攔截器添加到JSF生命周期中。
org.jboss.seam.jsf.SeamPhaseListener
navigation.xml文檔為多頁面應用包含JSF頁面導航規則。因為hello world示例只有一個簡單的頁面,因此該文檔是空的。
components.xml文檔包含Seam特有的配置選項,除 jndi-pattern屬性以外,其他都不依賴於應用。該屬性必須包括EAR文檔的名稱,以便Seam通過其的JNDI全名訪問EJB3 bean。
jndi-pattern="helloworld/#/local"
debug="false"/>
5.2. Seam組件JAR包 app.jar文檔包含所有的EJB3bean 類(實體bean和會話bean)以及EJB3相關的配置文檔。
app.jar
|+ Person.class // entity bean
|+ Manager.class // session bean interface
|+ ManagerAction.class // session bean
|+ seam.properties // empty file but needed
|+ META-INF
|+ ejb-jar.xml
|+ persistence.xml seam.properties文檔這兒是空但必需的,因為Jboss要通過它知道此JAR文件包含Seam EJB3 bean類,並且相應地處理註解。
ejb-jar.xml文檔包含額外的配置信息,這些信息能重載或者增補EJB3 bean上的註解。在一個Seam應用中,它能將所有的EJB3 類加入Seam攔截器。我們能在所有的Seam應用中重用該文檔。
*
org.jboss.seam.ejb.SeamInterceptor
persistence.xml文檔為EJB3 實體bean配置了後台數據源。本例中,我們只是使用了被嵌入到JBoss AS中默認的HSQL資料庫(也就是 java:/DefaultDS數據源)。
org.hibernate.ejb.HibernatePersistence
java:/DefaultDS
value="org.hibernate.dialect.HSQLDialect"/>
value="create-drop"/>
這樣,以上就是一個簡單Seam應用所需的所有配置和打包。我們將在以後討論到本書的更高級的主題時,涵蓋更多的配置選項和類庫。再次強調一下,Seam應用入門最簡單的方法就是,不要擔心這些配置文件,只需要從已有的應用模板做起。
這就是hello world 應用,三個簡單的Java類,一個JSF頁面,一組靜態配置文件。我們已經有了一個完整的資料庫驅動的web應用。整個應用只需要的是少於30行的Java代碼,沒有一處“XML代碼”。但是如果開發者有PHP背景,你可能仍然會問“何以這麼簡單?我能在php中使用更少的代碼嗎?”
好吧,答案就是Seam應用在理論上要比PHP(或者其他任何一種腳本語言)應用簡單的多。Seam 組件模式允許我們,有所控制地,可維護地給應用增加更多的功能。我們很快就會發現,Seam組件使開發有狀態的和有事務的web應用變得易如反掌。對象關係映射框架(例如:實體bean)允許我們將注意力放在抽象數據模型上,而不需要處理資料庫特有的SQL語句。
本文是基於該書的第一章和第二章。後面的章節中,我們繼續討論如何使用Seam組件繼續開發複雜的Seam 應用。參見本書目錄來查看本書的所有主題。
請看Gavin King的兩個訪談錄來查看以往關於Seam的話題。
JBoss Seam 1.1 Indepth: An Interview with Gavin King JBoss Seam 1.0: rethinking web application architecture