天天看點

SEAM學習(五)---Seam 元件

Seam 元件

Seam 元件是POJO(Plain Old Java Objects)。特别地,他們是JavaBean或者EJB 3.0 enterprise bean。Seam并不強求元件是EJB,甚至可以不使用EJB 3.0相容的容器,Seam在設計的時候處處考慮對EJB 3.0的支援,并且包含對EJB 3.0的深度整合。

*

EJB 3.0 stateless Session Beans

*

EJB 3.0 stateful Session Beans

*

EJB 3.0 entity beans

*

JavaBeans

*

EJB 3.0 message-driven beans

(1)無狀态Session Bean

無狀态Session Bean元件無法在多次調用之間保持狀态。是以,它們通常在不同的Seam上下文中,操作其他元件的狀态。他們可以作為JSF的action listener,但是不能為JSF元件的顯示提供屬性。

因為每次請求都産生一個新的執行個體,無狀态session bean可以并發通路。把其執行個體和請求相關聯是EJB3容器的責任(通常這些執行個體會從一個可重用的池中配置設定,是以你可能會發現某些執行個體變量還儲存着上次使用的痕迹。)

無狀态Session Bean總是生活在無狀态上下文中。

無狀态Session Bean是Seam元件中最沒趣的了。

Seam無狀态Session Bean元件可以使用 Component.getInstance() 或者 @In(create=true) 執行個體化。它們不能直接使用JNDI或者 new 操作執行個體化。

(2)有狀态Session Bean

有狀态Session Bean不僅可以在bean的多次調用之間保持狀态,而且在多次請求之間也可以保持狀态。不由資料庫儲存的狀态通常應該由有狀态Session Bean保持。這是Seam和其他web架構之間的一個顯著的不同點。其他架構把目前會話的資訊直接儲存在 HttpSession 中,而在Seam中你應該把它們儲存在有狀态Session Bean的執行個體中,該執行個體被綁定到會話上下文。這可以讓Seam來替你管理狀态的生命周期,并且保證在多個不同的并發會話中沒有狀态沖突。

有狀态Session Bean經常被作為JSF action listener使用,也可以作為JSF顯示或者form送出的backing bean(支援bean ,或稱背景bean),提供屬性供元件通路。

預設情況下,有狀态Session Bean會被綁定到Conversation Context。它們絕不會綁定到page或stateless context。

對Session範圍的有狀态Session Bean的并發請求,會被Seam按順序串行處理。

Seam有狀态Session Bean元件可以使用 Component.getInstance() 或者 @In(create=true) 執行個體化。它們不能直接使用JNDI或者 new 操作執行個體化。

(3) 實體Bean

實體Bean可以被綁定到上下文變量,起到Seam元件的作用。因為Entity除了上下文辨別之外,還有持久辨別,Entity實體通常明确的由Java Code綁定,而非由Seam隐性初始化。

Entity Bean實體不支援雙向注入或者上下文劃分。對Entity Bean的調用也不會觸發驗證。

Entity Bean通常不作為JSF的action listener使用,但經常作為JSF元件用于顯示或者form送出的背景bean,提供屬性功能。特别是,當Entity作為背景Bean的時候,它會和一個無狀态Session Bean扮演的action listener聯用,來實作CRUD之類的功能。

預設情況下,Entity Bean被綁定到Conversation Context。他們永遠不能被綁定到無狀态Context。

注意,在叢集環境中,把Entity Bean直接綁定到Conversation或者Session範圍的Seam上下文變量,與在有狀态Session Bean中保持一個對Entity Bean的引用相比,性能比較差。是以,并非所有的Seam應用程式都會把Entity Bean定義為Seam元件。

Seam實體Bean元件可以使用 Component.getInstance()、@In(create=true) 或者直接使用 new 操作來執行個體化。

(4) JavaBeans

JavaBeans可以像無狀态或者有狀态Session Bean那樣使用。但是,它們不能提供Session Bean那麼多的功能(聲明式事務劃分、聲明式安全性、高效的叢集狀态複制、EJB 3.0持久化、逾時方法等等)。

在後面有一章,我們會展示如何在沒有EJB容器的情況下使用Seam和Hibernate。此時,元件是JavaBeans,而非Session Beans。 但是注意,在很多應用伺服器中,對Conversation或Session 範圍的Seam JavaBean元件叢集操作,要比對有狀态Session Bean元件叢集慢。

預設,JavaBeans是綁定到Event Context的。

對Session範圍的JavaBeans的并發請求總是會被Seam轉化為串行執行。

Seam JavaBean元件可以使用 Component.getInstance()、@In(create=true) 或者直接使用 new 操作來執行個體化。

(5)消息驅動Bean

消息驅動Bean通常作為Seam元件。但是,消息驅動Bean與其他Seam元件的調用方式非常不同——它們并非通過Context變量調用,它們會監聽發送到JMS Queue或者Topic的消息。

消息驅動Bean不能被綁定到Seam上下文。它們也不能通路它們的“調用者”的Session或者會話狀态。但是,它們支援雙向注入和一些其他的Seam功能。

消息驅動Bean不會被應用執行個體化,它是在接受到一條消息時由EJB容器來完成執行個體化的。

(6) 攔截

為了表演Seam的魔術(雙向注入,上下文劃分,校驗等),它必須對元件調用進行攔截。對JavaBean而言,Seam可以完全控制元件的初始化,不需要特别的配置。對于Entity Bean,也不需要攔截器,因為雙向注入和上下文劃分不起作用。 對Session Bean,我們必須為它注冊EJB攔截器。我們可以使用注解,比如:

@Stateless

@Interceptors(SeamInterceptor.class)

public class LoginAction implements Login {

...

}

但是更好的辦法是在 ejb-jar.xml 中定義攔截器。

<interceptors>

<interceptor>

<interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

</interceptor>

</interceptors>

<assembly-descriptor>

<interceptor-binding>

<ejb-name>*</ejb-name>

<interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

</interceptor-binding>

</assembly-descriptor>

(7) 元件名字

所有Seam元件都需要名字。我們可以通過 @Name 注解來命名元件:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

...

}

這個名字是 seam component name,和EJB規範定義的任何其他名字都沒有關系。 但是,Seam元件名字就相當于JSF管理的Bean Name的名字,是以,可以了解為這兩個概念是等同的。

@Name 不是定義元件名稱的唯一方式,但是我們總得要在 某個地方 來指定名字。 否則,Seam 所有的注解部分就無法工作。

就如同在JSF中,Seam元件執行個體綁定成上下文變量時,其名字通常群組件名相同。 是以,例如我們可以通過 Contexts.getStatelessContext().get("loginAction") 來通路 LoginAction。 特别是,不管Seam自己何時初始化一個元件,它将這個新執行個體以元件的名字綁定成一個變量。 但是,又和JSF一樣,應用程式也可以把元件綁定成其他的上下文變量,隻需通過API程式設計調用。 例如,目前登入的使用者(User)可以被綁定成為Session上下文中的 currentUser 變量,而同時,另一個用作某種管理功能的使用者則被綁定成對話上下文的 user 變量。

對非常大型的應用程式,經常使用全限定名;内置的Seam元件就是這樣。

@Name("com.jboss.myapp.loginAction")

@Stateless

@Interceptors(SeamInterceptor.class)

public class LoginAction implements Login {

...

}

我們可以在Java代碼和JSF表達式語言中使用全限定的元件名稱。

<h:commandButton type="submit" value="Login"

action="#{com.jboss.myapp.loginAction.login}"/>

這很啰嗦,Seam也提供了把全限定名簡寫的辦法。在 components.xml 檔案中加入類似這樣的一行:

<factory name="loginAction" scope="STATELESS" value="#{com.jboss.myapp.loginAction}"/>

所有的Seam内置元件都有全限定名,但大多數都在Seam jar檔案的 components.xml 中簡寫為簡單的名字。

(8) 定義元件範圍(Defining the Component Scope)

我們可以使用 @Scope 注解來覆寫預設的元件範圍(上下文)。這可以讓我們定義元件執行個體被Seam初始化後綁定到的具體上下文。

@Name("user")

@Entity

@Scope(SESSION)

public class User {

...

}

org.jboss.seam.ScopeType定義了可能範圍的枚舉.

(9)具有多個角色的元件(Components with multiple roles)

有些Seam元件類可以在系統中具有多個角色。例如,我們經常有一個 User 類用作Session-Scoped元件,代表目前使用者,同時它又在使用者管理界面中被用作Conversation-Scoped元件。@Role 注解讓我們可以定義元件在另一個範圍中的額外角色名 —— 這可以讓我們把相同的元件類綁定成不同的上下文變量。(任何Seam元件 執行個體 都可以被綁定到多個上下文變量,但@Role使得我們也可以在類的級别做到這一點,進而享受自動執行個體化的優點。)

@Name("user")

@Entity

@Scope(CONVERSATION)

@Role(name="currentUser", scope=SESSION)

public class User {

...

}

@Roles 注解可以讓我們為元件指定任意多的附加角色。

@Name("user")

@Entity

@Scope(CONVERSATION)

@Roles({@Role(name="currentUser", scope=SESSION),

@Role(name="tempUser", scope=EVENT)})

public class User {

...

}

(10) 内置元件

和很多優秀的架構一樣,Seam自産自用,主要實作了一系列的内置Seam攔截器(後文詳述)和Seam元件。這讓應用程式在運作時和内置的元件互動變得很容易,甚至可以用自己編寫的實作來替換掉内置元件,由此來定制Seam的基本功能。内置元件在 org.jboss.seam.core 這個Seam 命名空間中定義,Java包名也是相同的。

像所有Seam元件一樣,内置元件也可以被注射,但是它們也提供了便利的instance()靜态方法:

FacesMessages.instance().add("Welcome back, #{user.name}!");