Table of Contents
Seam是一种企业级Java的应用程序框架。它的灵感源自下列原则:
Seam为你的应用程序中所有的业务逻辑定义了一种统一的组件模型。 Seam组件可能是有状态的,包含与几个定义良好的上下文中任何一个相关联的状态, 包括长时间运行上下文、持久化上下文、业务流程上下文, 以及用户交互中能够跨多个Web请求保存的对话上下文。
Seam中的表现层组件和业务逻辑组件之间并没有区别。 你可以根据你设计的任何架构给应用程序进行分层,而不是强制将你的应用程序逻辑硬塞进一个由你目前在使用的任何框架组合所强加给你的不适当的分层配置中。
与简单的Java EE或者J2EE组件不同,Seam组件可以同时访问与Web请求相关的状态,以及保存在事务资源中的状态(而不必通过方法参数手工传播Web请求状态)。 你可能反对说由旧式的J2EE平台强加给你的应用程序分层是件好东西,没有什么可以阻止你利用Seam创建一个相当的分层架构— 区别在于,你要自己架构应用程序,并决定有哪些层,以及它们是如何合作的。
JSF和EJB 3.0是Java EE5的两个最好的新特性。EJB3是服务器端业务和持久逻辑的全新组件模型。 同时,JSF也是表现层的一个优秀组件模型。不幸的是,这二者都无法独自解决所有的计算问题。 实际上,JSF和EJB3结合使用后运作得最好。 但是Java EE5规范并没有提供如何整合这两个组件模型的标准方法。 所幸,这两种模型的创建者都前瞻到了这种状况,并且提供了标准的扩展点,允许对各自进行扩展,或者与其他解决方案集成。
Seam将JSF和EJB3的组件模型合二为一,消除了胶合代码,使得开发者专注于业务问题。
编写“一切”都是EJB的Seam应用程序是有可能的。如果你习惯把EJB当作是细粒度的所谓“重量化”的对象,这可能会令你很吃惊。 然而,从开发人员的角度来看,3.0版本已经完全改变了EJB的本质。 EJB是一个细粒度的对象—没有什么东西会比注解的JavaBean更复杂了。Seam甚至鼓励你使用会话Bean作为JSF动作监听者!
另一方面,如果你宁可不在这个时候采用EJB 3.0,不用勉强。 事实上,任何Java类都可以是一个Seam组件,并且Seam提供了你期待从“轻量化”的容器,甚至任何组件、EJB或者其他东西中获得的所有功能。
Seam支持两个最好的、开源的基于JSF的AJAX解决方案:JBoss RichFaces和ICEfaces。 这两个解决方案让你无需编写任何JavaSctipt代码就可以为你的界面添加AJAX功能。
Seam也提供了内置的JavaSctipt远程访问层,它让你异步地从客户端JavaScript中调用组件,而不需要中间的action层。 你还可以订阅服务器端的JMS主题,并通过AJAX的push方法接收信息。
假若不是有Seam内置的并发和状态管理能力,以上这些方法将都无法很好地运作。 这两种方法确保服务器端能够安全而高效地处理多个并发细粒度的异步AJAX请求。
Seam可以选择通过jBPM提供透明的业务流程管理。使用jBPM和Seam共同实现复杂的工作流、合作和任务管理,简单到了让人难以置信的程度。
Seam甚至允许你利用与jBPM给业务流程定义所使用的相同语言(jPDL)来定义表现层页面流。
JSF为表示层提供了非常丰富的事件模型。 通过以完全相同的事件处理机制暴露与jBPM业务流程相关的事件,Seam强化了这一模型,这就为Seam的统一组件模型提供了统一的事件模型。
从EJB早期开始,我们已经习惯于声明式事务管理和J2EE声明式安全的概念。EJB 3.0还引入了声明式持久上下文管理。 一个更广泛的管理状态问题--管理与某个特殊context关联的状态,有三个特例,它确保这个上下文结束时进行所有必要的清理。 Seam把声明式状态管理的概念推进的远得多,并把它应用于应用程序状态(application state)。 J2EE应用程序一般通过手工实现状态管理,通过获取和设置Servlet Session和Request属性。 假若程序没能清除Session属性,或者在多窗口的应用程序中,与不同的工作流关联的Session数据发生冲突,这种状态管理的方法就会成为很多Bug和内存泄漏的根源。 Seam有可能几乎完全消除这类Bug。
声明式应用程序状态管理通过Seam定义的丰富的context model(上下文模型)而成为可能。 Seam扩展了Servlet规范—定义的上下文模型——请求、会话、应用程序—增加了两个新的上下文— 对话和业务流程—,从业务逻辑的角度来看它们更具意义。
一旦你开始使用对话,将会惊讶于许多事情变得更加容易了。你曾经在像Hibernate或者JPA这样的ORM解决方案中痛苦地处理过延迟的关联抓取吗? Seam对话范围的持久化上下文意味着你将几乎看不到 LazyInitializationException。 你曾经遇到过刷新(Refresh)按钮或者后退(Back)按钮的问题吗?或者有过重复提交表单的问题吗? 有通过post-then-redirect传播信息的问题吗?Seam的对话管理解决了这些问题,甚至无需你真正去关注它们。 它们都是自Web最早期以来普遍的不良状态管理架构的征兆。
Inversion of Control(控制反转) 或者 dependency injection(依赖注入) 的概念出现在JSF和EJB3以及很多所谓的“轻型容器”中。 这类容器大多注重于实现 stateless services(无状态服务) 的组件注射。 即便在支持对有状态的组件进行注射的情况下(例如JSF),事实上也难以用于处理应用程序状态, 因为有状态组件的范围难以有效而灵活地定义,并且属于更广范围的组件不能被注入到属于更窄范围的组件中。
Bijection(双向注入)和IoC的不同之处在于它是动态的、语境相关的以及双向的。 你可以把这一机制理解成将语境相关的变量(与当前线程绑定的各种上下文中的名称)对应到组件的属性中。 双向注入允许由容器对有状态的组件进行自动组装。它甚至允许组件可以安全而简单地处理上下文变量的值,只需要把它赋给组件的属性。
Seam应用程序让用户自由地在多个浏览器窗口中切换,每个窗口都与一个不同的、安全隔离的对话关联。 应用程序甚至可以利用 workspace management,允许用户在一个浏览器窗口的多个对话(工作区)之间进行切换。 Seam不仅提供正确的多窗口操作,还提供在一个窗口中模拟多个窗口的操作。
传统上,关于到底哪些元信息可以算作配置,Java社区一直处于一种极为混乱的状态。 J2EE和流行的“轻型”容器为真正可以在不同的系统部署之间配置的东西,以及任何不容易用Java表达的其他声明都提供了基于XML的部署描述符。 Java 5 注解改变了所有这一切。
EJB3.0 接受注解和“对例外配置”,这成了以声明的形式为容器提供信息的最简易方法。不幸的是,JSF仍然在十分依赖笨重的XML配置文件。 Seam扩展了EJB 3.0 提供的注解,以用于声明式状态管理和声明式上下文划分。 这让你摆脱了对繁琐的JSF managed bean(JSF受管bean)的配置,减少了所需的XML,只剩下那些真正属于XML的信息(JSF导航规则)。
Seam组件作为POJO,天生就是可以进行单元测试的。但是对于复杂的应用程序,只有单元测试则还不够。 对于Java Web应用程序来说,集成测试一般是一项笨拙且困难的任务。因此,Seam为Seam应用程序提供了可测试性作为该框架的一项核心功能。 你可以轻易地编写重现与用户完整交互的JUnit或TestNG测试,来演习除了视图View(JSP或者Facelets页面)之外的所有系统组件。 你可以直接从你的IDE中运行这些测试,Seam会在那里自动地利用JBoss Embeddable部署EJB组件。
我们认为最新的Java EE规范很不错。但是我们知道它还远不够完美。 在规范中有许多漏洞(例如,GET请求的JSF生命周期中的局限性),Seam修正了这些漏洞。 Seam的创建者们正与JCP专家组一道,确保这些修正恢复到标准的下一次修订中。
当今的Web框架认为太小了。它们让你叫用户输入表单,并进入到你的Java对象。然后它们就让你悬着。 真正完整的Web应用程序框架应该解决像持久化、并发、异步、状态管理、安全、电子邮件、信息、PDF和图表生成、工作流、wikitext渲染、Web Services、缓存等等更多的问题。 一旦你尝到了Seam的甜头,就会惊讶地发现许多问题都变得更加简单了......
Seam为持久化集成了JPA和Hibernate 3,为轻量化的异步性集成了EJB Timer Service和Quartz,为工作流集成了jBPM,为业务规则集成了JBoss规则,为电子邮件集成了Meldware Mail,为完整的文本搜索集成了Hibernate Search和Lucene,为消息集成了JMS,以及为页面片断捕捉集成了JBoss Cache。 Seam在JAAS和JBoss规则之上,创建了一个新的基于规则的安全框架。甚至有用来渲染PDF、在线电子邮件和图表及wikitext的JSF标签库。 Seam组件可以同时作为一个Web Service进行调用,异步地从客户端JavaScript或者Google Web Toolkit,或者当然也可以直接从JSF调用。
Seam在任何Java EE应用程序服务器中都可以运行,甚至在Tomcat中也可以。如果你的环境支持EJB 3.0,好极了! 如果不支持,也没关系,你可以使用Seam为持久化内置的包含JPA或者Hibernate3的事务管理。 或者,你可以在Tomcat中部署JBoss Embedded,同时享有对EJB 3.0 的完整支持。

最终你会发现,Seam、JSF和EJB3的组合就是用Java编写复杂Web应用程序的 最 简单办法。你不会相信所需的代码是多么地少!
本文档翻译由俞黎敏作为Leader组织。翻译及一审、二审名单见下。王琳、马越对全书进行了三审。俞黎敏进行了全书统稿及发布的build工作。
Table 1. 翻译及审核人员列表
| 章节 | KB | 页数 | 翻译 | 一审 | 二审 |
|---|---|---|---|---|---|
| master.xml | 17K | CaoXiaogang | Echo | YuLimin | |
| 1. tutorial.xml (1.1-1.4) | 130K | 20P | seanchan | Jacky | DigitalSonic |
| (1.5-1.11) | 24P | DigitalSonic | Jacky | ronghao | |
| 2. gettingstarted.xml | 21K | 6P | seanchan | zaya | DigitalSonic |
| 3. concepts.xml | 56K | 16P | CaoXiaogang | kuuyee | ronghao |
| 4. xml.xml | 25K | 7P | downpour | Echo | YuLimin |
| 5. events.xml | 39K | 11P | mochow | xihuyu2000 | Echo |
| 6. conversations.xml | 34K | 10P | magice | Echo | YuLimin |
| 7. jbpm.xml | 32K | 10P | 差沙 | ronghao | Echo |
| 8. persistence.xml | 23K | 6P | pesome | caoer | DigitalSonic |
| 9. validation.xml | 9K | 4P | pesome | DigitalSonic | YuLimin |
| 10. groovy.xml | 11K | 4P | kuuyee | DigitalSonic | YuLimin |
| 11. framework.xml | 20K | 7P | alexchang | CaoXiaogang | YuLimin |
| 12. drools.xml | 7K | 3P | DigitalSonic | shaozhou | Echo |
| 13. security.xml | 51K | 14P | YuLimin | xihuyu2000 | Echo |
| 14. i18n.xml | 14K | 4P | YY | DigitalSonic | Echo |
| 15. text.xml | 7K | 3P | DigitalSonic | yeshucheng(万国辉) | Echo |
| 16. itext.xml | 51K | 11P | lyfcdy | Echo | YuLimin |
| 17. mail.xml | 26K | 7P | chentianyi | yeshucheng(万国辉) | Echo |
| 18. jms.xml | 11K | 5P | YuLimin | caoer | Echo |
| 19. cache.xml | 11K | 3P | crazycy | CaoXiaogang | DigitalSonic |
| 20. webservices.xml | 9K | 3P | Echo | YuLimin | Echo |
| 21. remoting.xml | 37K | 13P | crazycy | agile_boy | Echo |
| 22. gwt.xml | 10K | 4P | yeshucheng(万国辉) | Echo | YuLimin |
| 23. spring.xml | 13K | 4P | YY | caoer | DigitalSonic |
| 24. hsearch.xml | 7K | 3P | yeshucheng(万国辉) | agile_boy | Echo |
| 25. configuration.xml | 48K | 15P | yeby | kuuyee | Echo |
| 26. annotations.xml | 64K | 14P | caoer | CaoXiaogang | YuLimin |
| 27. components.xml | 68K | 11P | jiaochar | zaya | HuYan |
| 28. controls.xml | 47K | 13P | Echo | YuLimin | Catherine |
| 29. elenhancements.xml | 5K | 2P | CaoXiaogang | yeshucheng(万国辉) | Echo |
| 30. testing.xml | 10K | 6P | agile_boy | CaoXiaogang | YuLimin |
| 31. tools.xml | 23K | 9P | junjzheng | CaoXiaogang | Echo |
| 32. oc4j.xml | 31K | 8P | yeshucheng(万国辉) | YuLimin | Echo |
| 33. dependencies.xml | 26K | 5P | yeshucheng(万国辉) | DigitalSonic | YuLimin |
本教程假定你已下载JBoss AS 4.0.5并安装了EJB 3.0 profile(请使用JBoss AS安装器)。你也得下载一份Seam并解压到工作目录上。
各示例的目录结构仿效以下形式:
网页、图片及样式表可在 examples/registration/view 目录中找到。
诸如部署描述文件及数据导入脚本之类的资源可在目录 examples/registration/resources 中找到。
Java源代码保存在 examples/registration/src 中。
Ant构建脚本放在 examples/registration/build.xml 文件中。
第一步,确保已安装Ant,并正确设定了 $ANT_HOME 及 $JAVA_HOME 的环境变量。接着在Seam的根目录下的 build.properties 文件中正确设定JBoss AS 4.0.5的安装路径。 若一切就绪,就可在JBoss的安装根目录下敲入 bin/run.sh 或 bin/run.bat 命令来启动JBoss AS。(译注:此外,请安装JDK1.5以上以便能直接运行示例代码)
现在只要在Seam安装目录 examples/registration 下输入 ant deploy 就可构建和部署示例了。
试着在浏览器中访问此链接:http://localhost:8080/seam-registration/。
首先,确保已安装Ant,并正确设定了 $ANT_HOME 及 $JAVA_HOME 的环境变量。接着在Seam的根目录下的 build.properties 文件中正确设定Tomcat 6.0的安装路径。你需要按照25.5.1章节“安装嵌入式的Jboss”中的指导配置 (当然, SEAM也可以脱离Jboss在TOMCAT上直接运行)。
至此,就可在Seam安装目录 examples/registration 中输入 ant deploy.tomcat 构建和部署示例了。
最后启动Tomcat。
试着在浏览器中访问此链接:http://localhost:8080/jboss-seam-registration/。
当你部署示例到Tomcat时,任何的EJB3组件将在JBoss的可嵌入式的容器,也就是完全独立的EJB3容器环境中运行。
注册示例是个极其普通的应用,它可让新用户在数据库中保存自己的用户名,真实的姓名及密码。 此示例并不想一下子就把Seam的所有的酷功能全部秀出。然而, 它演示了EJB3 会话Bean作为JSF动作监听器及Seam的基本配置的使用方法。
或许你对EJB 3.0还不太熟悉,因此我们会对示例的慢慢深入说明。
此示例的首页显示了一个非常简单的表单,它有三个输入字段。试着在表单上填写内容并提交,一旦输入数据被提交后就会在数据库中保存一个user对象。

本示例由两个JSP页面,一个实体Bean及无状态的会话Bean来实现。

让我们看一下代码,就从最“底层”的实体Bean开始吧。
我们需要EJB 实体Bean来保存用户数据。这个类通过注解声明性地定义了 persistence 及 validation 属性。它也需要一些额外的注解来将这个类定义为Seam的组件。
Example 1.1.
@Entity@Name("user")
@Scope(SESSION)
@Table(name="users")
public class User implements Serializable { private static final long serialVersionUID = 1881413500711441951L; private String username;
private String password; private String name; public User(String name, String password, String username) { this.name = name; this.password = password; this.username = username; } public User() {}
@NotNull @Length(min=5, max=15)
public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @NotNull public String getName() { return name; } public void setName(String name) { this.name = name; } @Id @NotNull @Length(min=5, max=15)
public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
![]() | EJB3标准注解 @Entity 表明了 User 类是个实体Bean. |
![]() | Seam组件需要一个 组件名称,此名称由注解 @Name来指定。此名称必须在Seam应用内唯一。当JSF用一个与组件同名的名称去请求Seam来解析上下文变量, 且该上下文变量尚未定义(null)时,Seam就将实例化那个组件,并将新实例绑定给上下文变量。 在此例中,Seam将在JSF第一次遇到名为 user 的变量时实例化 User。 |
![]() | 每当Seam实例化一个组件时,它就将始化后的实例绑定给组件中 默认上下文 的上下文变量。默认的上下文由 @Scope注解指定。 User Bean是个会话作用域的组件。 |
![]() | EJB标准注解@Table 表明了将 User 类映射到 users 表上。 |
![]() | name、 password 及 username 都是实体Bean的持久化属性。所有的持久化属性都定义了访问方法。当JSF渲染输出及更新模型值阶段时需要调用该组件的这些方法。 |
![]() | EJB和Seam都要求有空的构造器。 |
![]() | @NotNull 和 @Length 注解是Hibernate Validator框架的组成部份, Seam集成了Hibernate Validator并让你用它来作为数据校验(尽管你可能并不使用Hibernate作为持久化层)。 |
![]() | 标准EJB注解 @Id 表明了实体Bean的主键属性。 |
这个例子中最值得注意的是 @Name 和 @Scope 注解,它们确立了这个类是Seam的组件。
接下来我们将看到 User 类字段在更新模型值阶段时直接被绑定给JSF组件并由JSF操作, 在此并不需要冗余的胶水代码来在JSP页面与实体Bean域模型间来回拷贝数据。
然而,实体Bean不应该进行事务管理或数据库访问。故此,我们无法将此组件作为JSF动作监听器,因而需要会话Bean。
在Seam应用中大都采用会话Bean来作为JSF动作监听器(当然我们也可选择JavaBean)。
在我们的应用程序中确实存在一个JSF动作和一个会话Bean方法。在此示例中,只有一个JSF动作,并且我们使用会话Bean方法与之相关联并使用无状态Bean,这是由于所有与动作相关的状态都保存在 User Bean中。
这是示例中比较有趣的代码部份:
Example 1.2.
@Stateless@Name("register") public class RegisterAction implements Register { @In
private User user; @PersistenceContext
private EntityManager em; @Logger
private Log log; public String register()
{ List existing = em.createQuery( "select username from User where username=#{user.username}")
.getResultList(); if (existing.size()==0) { em.persist(user); log.info("Registered new user #{user.username}");
return "/registered.jsp";
} else { FacesMessages.instance().add("User #{user.username} already exists");
return null; } } }
![]() | EJB标准注解 @Stateless 将这个类标记为无状态的会话Bean。 |
![]() | 注解 @In将Bean的一个属性标记为由Seam来注入。 在此例中,此属性由名为 user 的上下文变量注入(实例的变量名)。 |
![]() | EJB标准注解 @PersistenceContext 用来注入EJB实体管理器。 |
![]() | Seam的 @Logger 注解用来注入组件的 Log 实例。 |
![]() | 动作监听器方法使用标准的EJB3 EntityManager API来与数据库交互,并返回JSF的输出结果。 请注意,由于这是个会话Bean,因此当 register() 方法被调用时事务就会自动开始,并在结束时提交(commit)。 |
![]() | 请注意Seam让你在EJB-QL中使用JSF EL表达式。因此可在标准JPA Query 对象上调用普通的JPA setParameter() 方法,这样岂不妙哉? |
![]() | Log API为显示模板化的日志消息提供了便利。 |
![]() | 多个JSF动作监听器方法返回一个字符串值的输出,它决定了接下来应显示的页面内容。 空输出(或返回值为空的动作监听器方法)重新显示上一页的内容。 在普通的JSF中,用JSF的导航规则(navigation rule) 来决定输出结果的JSF视图id是很常用的。 这种间接性对于复杂的应用是非常有用的,值得去实践。但是,对于象示例这样简单的的应用,Seam让你使用JSF视图id作为输出结果,以减少对导航规则的需求。请注意,当你用视图id作为输出结果时,Seam总会执行一次浏览器的重定向。 |
![]() | Seam提供了大量的 内置组件(built-in components) 来协助解决那些经常遇到的问题。 用 FacesMessages 组件就可很容易地来显示模板化的错误或成功的消息。 内置的Seam组件还可由注入或通过调用 instance() 方法来获取。 |
这次我们并没有显式指定 @Scope,若没有显式指定时,每个Seam 组件类型就使用其默认的作用域。对于无状态的会话Bean, 其默认的作用域就是无状态的上下文。实际上 所有的 无状态的会话Bean都属于无状态的上下文。
会话Bean的动作监听器在此小应用中履行了业务和持久化逻辑。在更复杂的应用中,我们可能要将代码分层并重构持久化逻辑层成 专用数据存取组件,这很容易做到。但请注意Sean并不强制你在应用分层时使用某种特定的分层策略。
此外,也请注意我们的SessionBean会同步访问与web请求相关联的上下文(比如在 User 对象中的表单的值),状态会被保持在事务型的资源里(EntityManager 对象)。 这是对传统J2EE的体系结构的突破。再次说明,如果你习惯于传统J2EE的分层,也可以在你的Seam应用实行。但是对于许多的应用,这是明显的没有必要 。
很自然,我们的会话Bean需要一个本地接口。
所有的Java代码就这些了,现在去看一下部署描述文件。
如果你此前曾接触过许多的Java框架,你就会习惯于将所有的组件类放在某种XML文件中来声明,那些文件就会随着项目的不断成熟而不断加大到最终到不可收拾的地步。 对于Seam应用,你尽可放心,因为它并不要求应用组件都要有相应的XML。大部份的Seam应用要求非常少量的XML即可,且XML文件大小不会随着项目的增大而快速增长。
无论如何,若能为 某些 组件(特别是Seam内置组件)提供某些 外部配置往往是有用的。这样一来,我们就有几个选择, 但最灵活的选择还是使用位于 WEB-INF 目录下的 components.xml 配置文件。 我们将用 components.xml 文件来演示Seam怎样在JNDI中找到EJB组件:
Example 1.4.
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core">
<core:init jndi-pattern="@jndiPattern@"/>
</components>此代码配置了Seam内置组件 org.jboss.seam.core.init 的 jndiPattern 属性。这里需要奇怪的@符号是因为ANT脚本会在部署应用时将正确的JNDI语法在标记处自动填补
我们将以WAR的形式来部署此小应用的表示层,因此需要web部署描述文件。
Example 1.5.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- Seam -->
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
<!-- MyFaces -->
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
</web-app>此 web.xml 文件配置了Seam和JSF。所有Seam应用中的配置与此处的配置基本相同。
绝大多数的Seam应用将JSF来作为表示层。因而我们通常需要 faces-config.xml。SEAM将用Facelet定义视图表现层,所以我们需要告诉JSF用Facelet作为它的模板引擎。
Example 1.6.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config
PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
<!-- A phase listener is needed by all Seam applications -->
<lifecycle>
<phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>
</faces-config>注意我们不需要申明任何JSF managed Bean!因为我们所有的managed Bean都是通过经过注释的Seam组件。所以在Seam的应用中,faces-config.xml比原始的JSF更少用到。
实际上,一旦你把所有的基本描述文件配置完毕,你所需写的 唯一类型的 XML文件就是导航规则及可能的jBPM流程定义。对于Seam而言, 流程(process flow) 及 配置数据 是唯一真正属于需要XML定义的。
在此简单的示例中,因为我们将视图页面的ID嵌入到Action代码中,所以我们甚至都不需要定义导航规则。
ejb-jar.xml 文件将 SeamInterceptor 绑定到压缩包中所有的会话Bean上,以此实现了Seam与EJB3的整合。
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
version="3.0">
<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>
</ejb-jar>persistence.xml 文件告诉EJB的持久化层在哪找到数据源,该文件也含有一些厂商特定的设定。此例在程序启动时自动创建数据库Schema。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="userDatabase">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>对于Seam应用的视图可由任意支持JSF的技术来实现。在此例中,我们使用了JSP,因为大多数的开发人员都很熟悉, 且这里并没有其它太多的要求。(我们建议你在实际开发中使用Facelets)。
Example 1.7.
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>
<html>
<head>
<title>Register New User</title>
</head>
<body>
<f:view>
<h:form>
<table border="0">
<s:validateAll>
<tr>
<td>Username</td>
<td><h:inputText value="#{user.username}"/></td>
</tr>
<tr>
<td>Real Name</td>
<td><h:inputText value="#{user.name}"/></td>
</tr>
<tr>
<td>Password</td>
<td><h:inputSecret value="#{user.password}"/></td>
</tr>
</s:validateAll>
</table>
<h:messages/>
<h:commandButton type="submit" value="Register" action="#{register.register}"/>
</h:form>
</f:view>
</body>
</html>这里的 <s:validateAll>标签是Seam特有的。 该JSF组件告诉JSF让它用实体Bean中所指定的Hibernat验证器注解来验证所有包含输入的字段。
Example 1.8.
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Successfully Registered New User</title>
</head>
<body>
<f:view>
Welcome, <h:outputText value="#{user.name}"/>,
you are successfully registered as <h:outputText value="#{user.username}"/>.
</f:view>
</body>
</html>这是个极其普通的使用JSF组件的JSP页面,与Seam毫无相干。
最后,因为我们的应用是要部署成EAR的,因此我们也需要部署描述文件。
Example 1.9.
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
version="5">
<display-name>Seam Registration</display-name>
<module>
<web>
<web-uri>jboss-seam-registration.war</web-uri>
<context-root>/seam-registration</context-root>
</web>
</module>
<module>
<ejb>jboss-seam-registration.jar</ejb>
</module>
<module>
<java>jboss-seam.jar</java>
</module>
<module>
<java>el-api.jar</java>
</module>
<module>
<java>el-ri.jar</java>
</module>
</application>此部署描述文件联接了EAR中的所有模块,并把Web应用绑定到此应用的首页 /seam-registration。
至此,我们了解了整个应用中 所有的 部署描述文件!
当提交表单时,JSF请求Seam来解析名为 user 的变量。由于还没有值绑定到 user 上(在任意的Seam上下文中), Seam就会实例化 user组件,接着把它保存在Seam会话上下文后,然后将 User 实体Bean实例返回给JSF。
表单输入的值将由在 User 实体中所指定的Hibernate验证器来验证。 若有非法输入,JSF就重新显示当前页面。否则,JSF就将输入值绑定到 User 实体Bean的字段上。
接着,JSF请求Seam来解析变量 register。 Seam在无状态上下文中找到 RegisterAction 无状态的会话Bean并把它返回。JSF随之调用 register() 动作监听器方法。
Seam拦截方法调用并在继续调用之前从Seam会话上下文注入 User 实体。
register() 方法检查所输入用户名的用户是否已存在。 若存在该用户名,则错误消息进入 facesmessages 组件队列,返回无效结果并触发浏览器重显页面。facesmessages 组件嵌在消息字符串的JSF表达式,并将JSF facesmessage 添加到视图中。
若输入的用户不存在,"/registered.jsp" 输出就会将浏览器重定向到 registered.jsp 页。 当JSF来渲染页面时,它请求Seam来解析名为 user 的变ດ