读书人

Seam学习( 2) seam组件

发布时间: 2012-09-09 09:27:54 作者: rapoo

Seam学习( 二) seam组件
1.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
EJB 3.0 message-driven beans
JavaBeans
2.Seam组件的种类.
(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)color=darkblue]消息驱动Bean [/color]
消息驱动Bean通常作为Seam组件。但是,消息驱动Bean与其他Seam组件的调用方式非常不同——它们并非通过Context变量调用,它们会监听发送到JMS Queue或者Topic的消息。

消息驱动Bean不能被绑定到Seam上下文。它们也不能访问它们的“调用者”的Session或者会话状态。但是,它们支持双向注入和一些其他的Seam功能。

消息驱动Bean不会被应用实例化,它是在接受到一条消息时由EJB容器来完成实例化的。
3. Seam组件的拦截

为了表演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>

4.Seam组件的命名规则.

所有Seam组件都需要名字。可以通过 @Name 注解来命名组件,组件名称为类名首字母小写,这样,就标识该类为Seam component:

@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 中简写为简单的名字。
5.定义Seam组件范围—efining the Component Scope)

我们可以使用 @Scope 注解来覆盖默认的组件范围(上下文)。这可以让我们定义组件实例被Seam初始化后绑定到的具体上下文。

@Name("user")
@Entity
@Scope(SESSION)
public class User {
...
}

org.jboss.seam.ScopeType定义了可能范围的枚举.
6. 具有多个角色的组件(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 {
...
}

7.内置组件

和很多优秀的框架一样,Seam自产自用,主要实现了一系列的内置Seam拦截器(后文详述)和Seam组件。这让应用程序在运行时和内置的组件交互变得很容易,甚至可以用自己编写的实现来替换掉内置组件,由此来定制Seam的基本功能。内置组件在 org.jboss.seam.core 这个Seam 命名空间中定义,Java包名也是相同的。

像所有Seam组件一样,内置组件也可以被注射,但是它们也提供了便利的instance()静态方法:

读书人网 >软件架构设计

热点推荐