Seam - 语境相关的组件 [满江红20080327]

Java EE框架

2.0GA


Table of Contents

JBoss Seam简介
1. Seam 入门
1.1. 试试看
1.1.1. 在JBoss AS上运行示例
1.1.2. 在Tomcat服务器上运行示例
1.1.3. 运行测试
1.2. 第一个例子:注册示例
1.2.1. 了解代码
1.2.1.1. 实体Bean:User.java
1.2.1.2. 无状态会话Bean:RegisterAction.java
1.2.1.3. 会话Bean的本地接口:Register.java
1.2.1.4. Seam组件部署描述文件:components.xml
1.2.1.5. Web部署描述文件:web.xml
1.2.1.6. JSF配置:faces-config.xml
1.2.1.7. EJB部署描述文件:ejb-jar.xml
1.2.1.8. EJB持久化部署描述文件:persistence.xml
1.2.1.9. 视图:register.xhtml 和 registered.xhtml
1.2.1.10. EAR部署描述文件:application.xml
1.2.2. 工作原理
1.3. Seam中的可点击列表:消息示例
1.3.1. 理解代码
1.3.1.1. 实体Bean:Message.java
1.3.1.2. 有状态的会话Bean:MessageManagerBean.java
1.3.1.3. 会话Bean的本地接口:MessageManager.java
1.3.1.4. 视图:messages.jsp
1.3.2. 工作原理
1.4. Seam和jBPM:待办事项列表(todo list)示例
1.4.1. 理解代码
1.4.2. 工作原理
1.5. Seam页面流:猜数字范例
1.5.1. 理解代码
1.5.2. 工作原理
1.6. 一个完整的Seam应用程序:宾馆预订范例
1.6.1. 介绍
1.6.2. 预订系统概况
1.6.3. 理解Seam业务对话(Conversation)
1.6.4. Seam的UI控制库
1.6.5. Seam调试页面
1.7. 一个使用Seam和jBPM的完整范例:DVD商店
1.8. 结合Seam和Hibernate的范例:Hibernate预订系统
1.9. 一个RESTful的Seam应用程序:Blog范例
1.9.1. 使用“拉”风格的MVC
1.9.2. 可收藏的搜索结果页面
1.9.3. 在RESTful应用程序中使用“推”风格("push"-style)的MVC
2. 用Seam-gen起步
2.1. 准备活动
2.2. 建立一个新的Eclipse项目
2.3. 创建新动作
2.4. 创建有动作的表单(form)
2.5. 从现有数据库生成应用程序
2.6. 将应用部署为EAR
2.7. Seam与增量热部署
2.8. 在Jboss 4.0下使用Seam
2.8.1. 安装JBoss 4.0
2.8.2. 安装JSF 1.2 RI
3. 上下文相关的组件模型
3.1. Seam上下文
3.1.1. Stateless context(无状态上下文)
3.1.2. Event context(事件上下文)
3.1.3. Page context(页面上下文)
3.1.4. Conversation context(业务会话上下文)
3.1.5. Session context(Session上下文)
3.1.6. Business process context (业务流程上下文)
3.1.7. Application context(应用上下文)
3.1.8. Context variables(上下文变量)
3.1.9. Context搜索优先级
3.1.10. 并发模型
3.2. Seam 组件
3.2.1. 无状态Session Bean
3.2.2. 有状态Session Bean
3.2.3. 实体Bean
3.2.4. JavaBeans
3.2.5. 消息驱动Bean
3.2.6. 拦截
3.2.7. 组件名字
3.2.8. 定义组件范围(Defining the Component Scope)
3.2.9. 具有多个角色的组件(Components with multiple roles)
3.2.10. 内置组件
3.3. 双向注入
3.4. Lifecycle methods(生命周期方法)
3.5. 条件装载(Conditional installation)
3.6. 日志
3.7. Mutable接口和@ReadOnly
3.8. Factory和Manager组件
4. 配置Seam组件
4.1. 通过属性设置来配置组件
4.2. 通过 components.xml 来配置组件
4.3. 细粒度的配置文件
4.4. 可配置的属性类型
4.5. 使用XML命名空间
5. 事件、拦截器和异常处理
5.1. Seam事件
5.1.1. 页面动作
5.1.1.1. 页面参数
5.1.1.2. 导航
5.1.1.3. 导航的定义、页面动作和参数的细粒度文件
5.1.2. 组件驱动的事件
5.1.3. 上下文事件
5.2. Seam 拦截器
5.3. 管理异常
5.3.1. 异常和事务
5.3.2. 激活Seam异常处理
5.3.3. 使用注解处理异常
5.3.4. 用XML处理异常
5.3.5. 一些常见的异常
6. 对话以及工作区管理
6.1. Seam的对话模型
6.2. 嵌套对话
6.3. 使用GET请求来开始一个对话
6.4. 利用<s:link>以及<s:button>
6.5. 成功信息
6.6. 使用“显式”的对话id
6.7. 工作区管理
6.7.1. 工作区管理及JSF导航
6.7.2. 工作区管理和jPDL页面流
6.7.3. 对话转换器
6.7.4. 对话列表
6.7.5. 导航控件
6.8. 对话组件和JSF组件绑定
6.9. 对话组件的并发调用
6.9.1. RichFaces Ajax
7. 页面流和业务流程
7.1. Seam中的页面流
7.1.1. 两种导航模型
7.1.2. Seam和后退按钮
7.2. 使用jPDL页面流
7.2.1. 安装页面流
7.2.2. 开始页面流
7.2.3. 页面节点和跳转
7.2.4. 流程控制
7.2.5. 流程的结束
7.2.6. 页面流组合
7.3. Seam中的业务流程管理
7.4. 使用jPDL业务流程定义
7.4.1. 安装流程定义
7.4.2. 初始化Actor id
7.4.3. 启动一个业务流程
7.4.4. 任务分配
7.4.5. 任务列表
7.4.6. 执行一个任务
8. Seam和对象/关系映射
8.1. 简介
8.2. Seam管理的事务
8.2.1. 关闭Seam管理的事务
8.2.2. 配置Seam事务管理器
8.2.3. 事务同步
8.3. Seam管理的持久化上下文
8.3.1. 在Seam管理的持久化上下文中使用JPA
8.3.2. 使用Seam管理的Hibernate会话
8.3.3. Seam管理的持久化上下文和原子会话
8.4. 使用JPA “代理(delegate)”
8.5. 在EJB-QL/HQL中使用EL
8.6. 使用Hibernate过滤器
9. Seam中的JSF表单验证
10. Groovy集成
10.1. Groovy简介
10.2. 用Groovy编写Seam应用
10.2.1. 编写Groovy组件
10.2.1.1. 实体
10.2.1.2. Seam组件
10.2.2. seam-gen
10.3. 部署
10.3.1. 部署Groovy代码
10.3.2. 开发时部署本地.groovy文件
10.3.3. seam-gen
11. Seam应用程序框架
11.1. 简介
11.2. Home对象
11.3. Query对象
11.4. Controller对象
12. Seam和JBoss规则
12.1. 安装规则
12.2. 在Seam组件中使用规则
12.3. 在jBPM流程定义中使用规则
13. 安全
13.1. 概述
13.1.1. 哪种模式更适合我的应用程序呢?
13.2. 需求
13.3. 取消安全
13.4. 验证
13.4.1. 配置
13.4.2. 编写验证方法
13.4.2.1. Identity.addRole()
13.4.3. 编写登录表单
13.4.4. 简化配置 - 概述
13.4.5. 处理安全异常
13.4.6. 登录重定向
13.4.7. HTTP验证
13.4.7.1. 编写Digest验证者
13.4.8. 高级验证特性
13.4.8.1. 使用容器的JAAS配置
13.5. 错误消息
13.6. 授权
13.6.1. 核心概念
13.6.2. 保护组件
13.6.2.1. @Restrict注解
13.6.2.2. 行内限制
13.6.3. 用户界面中的安全
13.6.4. 保护页面
13.6.5. 保护实体
13.6.5.1. 使用JPA的实体安全
13.6.5.2. 使用Hibernate的实体安全
13.7. 编写安全规则
13.7.1. 许可概述
13.7.2. 配置规则文件
13.7.3. 创建安全规则文件
13.7.3.1. 通配符许可检查
13.8. SSL安全
13.9. 实现Captcha测试
13.9.1. 配置Captcha Servlet
13.9.2. 添加Captcha到页面
13.9.3. 定制Captcha图片
14. 国际化和主题
14.1. 本地化
14.2. 标签
14.2.1. 定义标签
14.2.2. 标签显示
14.2.3. Faces Messages
14.3. 时区
14.4. 主题
14.5. 使用cookie保存locale和主题设置
15. Seam Text
15.1. 基本格式化
15.2. 输入代码和有特殊字符的文本
15.3. 链接
15.4. 输入HTML
16. iText PDF生成
16.1. 使用PDF支持
16.1.1. 创建一个文档
16.1.2. 基本的文本元素
16.1.3. 页眉和页脚
16.1.4. 章节
16.1.5. 列表
16.1.6. 表格
16.1.7. 文档常量
16.1.7.1. 颜色值
16.1.7.2. 对齐方式值
16.1.8. iText配置
16.2. 图表
16.3. 柱状图编码
16.4. 更详细的文档
17. 电子邮件
17.1. 创建一条消息
17.1.1. 附件
17.1.2. HTML/Text 交替部分
17.1.3. 多个收件人
17.1.4. 多条信息
17.1.5. 模板
17.1.6. 国际化
17.1.7. 其它的标识头
17.2. 接收邮件
17.3. 配置
17.3.1. mailSession
17.3.1.1. 在JBoss AS中查找JNDI
17.3.1.2. Seam配置会话
17.4. Meldware
17.5. 标签
18. 异步和消息
18.1. 异步
18.1.1. 异步方法
18.1.2. 包含Quartz Dispatcher的异步方法
18.1.3. 异步事件
18.2. Seam中的消息
18.2.1. 配置
18.2.2. 发送消息
18.2.3. 利用消息驱动Bean接收消息
18.2.4. 在客户端接收消息
19. 缓存
19.1. 在Seam中使用JBossCache
19.2. 页片段缓存
20. Web Services
20.1. 配置和打包
20.2. 对话的Web Services
20.2.1. 建议策略
20.3. Web Servic范例
21. Remoting
21.1. 配置
21.2. Seam对象
21.2.1. Hello World示例
21.2.2. Seam.Component
21.2.2.1. Seam.Component.newInstance()
21.2.2.2. Seam.Component.getInstance()
21.2.2.3. Seam.Component.getComponentName()
21.2.3. Seam.Remoting
21.2.3.1. Seam.Remoting.createType()
21.2.3.2. Seam.Remoting.getTypeName()
21.3. EL表达式求值
21.4. 客户端接口
21.5. 上下文
21.5.1. 设置和读取对话ID
21.5.2. 当前对话范围内的远程调用
21.6. 批量请求
21.7. 使用数据类型
21.7.1. 原生 / 基本 类型
21.7.1.1. String
21.7.1.2. Number
21.7.1.3. Boolean
21.7.2. JavaBeans
21.7.3. Date和Time
21.7.4. Enums 枚举类型
21.7.5. Collections 集合
21.7.5.1. Bags
21.7.5.2. Maps
21.8. 调试
21.9. 加载消息
21.9.1. 修改信息
21.9.2. 隐藏加载信息
21.9.3. 自定义加载指示器
21.10. 控制返回数据
21.10.1. 一般字段的约束
21.10.2. 集合和映射的约束
21.10.3. 特定类型对象的约束
21.10.4. 组合约束
21.11. JMS消息
21.11.1. 配置
21.11.2. 订阅JMS主题
21.11.3. 退订主题
21.11.4. 调整轮询过程
22. Seam和Google的Web工具包(GWT)
22.1. 配置
22.2. 准备你的组件
22.3. 将GWT小组件接到Seam组件
22.4. GWT Ant Targets
23. Spring Framework集成
23.1. 把Seam组件注入Spring Bean中
23.2. 将Spring Bean注入到Seam组件中
23.3. 将Spring Bean转换为Seam组件
23.4. Seam作用域的Spring Bean
23.5. 使用Spring PlatformTransactionManagement
23.6. 在Spring中使用Seam管理的持久化上下文
23.7. 在Spring中使用Seam管理的Hibernate会话
23.8. 作为Seam组件的Spring应用上下文
23.9. 使用Spring TaskExecutor的@Asynchronous
24. Hibernate Search
24.1. 简介
24.2. 配置
24.3. 用法
25. Seam配置和Seam应用程序打包
25.1. Seam基本配置
25.1.1. 将Seam与JSF和servlet容器集成
25.1.2. 使用Facelets
25.1.3. Seam Resource Servlet
25.1.4. Seam Servlet过滤器
25.1.4.1. 异常处理
25.1.4.2. 通过重定向传播对话
25.1.4.3. 多重表单提交
25.1.4.4. 字符编码
25.1.4.5. RichFaces
25.1.4.6. Identity Logging
25.1.4.7. 定制Servlet的上下文管理
25.1.4.8. 增加定制的过滤器
25.1.5. 将Seam与你的EJB容器集成
25.1.6. 切记!
25.2. 在Java EE 5中配置Seam
25.2.1. 打包
25.3. 在J2EE中配置Seam
25.3.1. 在Seam中引导Hibernate
25.3.2. 在Seam中引导JPA
25.3.3. 打包
25.4. 在Java SE中配置Seam,没有内嵌JBoss
25.5. 用嵌入式的JBoss在Java SE中配置Seam
25.5.1. 安装嵌入式的JBoss
25.5.2. 打包
25.6. 在Seam中配置jBPM
25.6.1. 打包
25.7. 在Portal中配置Seam
25.8. 在JBoss AS中配置SFSB和会话超时
26. Seam on OC4J
26.1. jee5/booking 实例
26.1.1. 预订酒店实例的依赖包
26.1.2. OC4J需要的额外依赖包
26.1.3. 配置文件的改变
26.1.4. 构建 jee5/booking 实例
26.2. 部署Seam应用程序到OC4J中
26.3. 将一个使用 seam-gen 创建的应用程序部署到OC4J中。
26.3.1. seam-gen之类的应用程序的OC4J部署描述符
27. Seam注解
27.1. 用于定义组件的注解
27.2. 用于双向注入的注解
27.3. 关于组件生命周期方法的注解
27.4. 用于声明上下文的注解
27.5. 用于在J2EE环境中使用Seam JavaBean组件的注解
27.6. 用于异常的注解
27.7. 用于Seam Remoting 的注解
27.8. 用于Seam拦截器(interceptor)的注解
27.9. 用于异步(asynchronicity)的注解
27.10. 用于JSF的注解
27.10.1. 和 dataTable 一起使用的注解
27.11. 用于数据绑定的元数据注解
27.12. 用于打包(packing)的注解
27.13. 用于和Servlet容器集成的注解
28. 内置Seam组件
28.1. 上下文注入组件
28.2. 工具组件
28.3. 组件的国际化和主题
28.4. 控制对话组件
28.5. 与jBPM相关的组件
28.6. 与安全相关的组件
28.7. 与JMS相关的组件
28.8. 与邮件相关的组件
28.9. 基础组件
28.10. 杂项组件
28.11. 特殊组件
29. Seam的JSF控件
29.1. 标签
29.2. 注解
30. 表达式语言增强
30.1. 参数方法绑定
30.1.1. 用法
30.1.2. 限制
30.1.2.1. 与JSP 2.1不兼容
30.1.2.2. 从Java代码中调用 MethodExpression
30.2. 参数值绑定
30.3. 映射
31. 测试Seam应用程序
31.1. Seam组件的单元测试
31.2. Seam组件的集成测试
31.2.1. 在集成测试中使用Mock对象
31.3. 集成测试Seam应用程序中的用户交互
31.3.1. 利用Mock数据进行集成测试
31.3.2. Seam Mail集成测试
32. Seam工具
32.1. jBPM设计器和查看器
32.1.1. 业务流程设计器
32.1.2. Pageflow查看器
33. 依赖包
33.1. 项目依赖包
33.1.1. Core
33.1.2. Ajax4JSF / RichFaces
33.1.3. Seam Mail
33.1.4. Seam PDF
33.1.5. JBoss Rules
33.1.6. JBPM
33.1.7. GWT
33.1.8. Spring
33.1.9. Groovy
33.2. 使用Maven依赖管理
A. Seam 2.0 开发手册中文翻译项目
A.1. 声明
A.2. 项目历程
A.2.1. Seam 1.2.1 开发手册翻译项目
A.2.2. Seam 2.0 Beta 1 开发手册翻译项目
A.2.3. Seam 2.0 正式版开发手册翻译项目

JBoss Seam简介

Seam是一种企业级Java的应用程序框架。它的灵感源自下列原则:

只有一种“工具”

Seam为你的应用程序中所有的业务逻辑定义了一种统一的组件模型。 Seam组件可能是有状态的,包含与几个定义良好的上下文中任何一个相关联的状态, 包括长时间运行上下文、持久化上下文、业务流程上下文, 以及用户交互中能够跨多个Web请求保存的对话上下文

Seam中的表现层组件和业务逻辑组件之间并没有区别。 你可以根据你设计的任何架构给应用程序进行分层,而不是强制将你的应用程序逻辑硬塞进一个由你目前在使用的任何框架组合所强加给你的不适当的分层配置中。

与简单的Java EE或者J2EE组件不同,Seam组件可以同时访问与Web请求相关的状态,以及保存在事务资源中的状态(而不必通过方法参数手工传播Web请求状态)。 你可能反对说由旧式的J2EE平台强加给你的应用程序分层是件好东西,没有什么可以阻止你利用Seam创建一个相当的分层架构— 区别在于,要自己架构应用程序,并决定有哪些层,以及它们是如何合作的。

将JSF与EJB 3.0整合

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或者其他东西中获得的所有功能。

集成AJAX

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最早期以来普遍的不良状态管理架构的征兆。

Bijection(双向注入)

Inversion of Control(控制反转) 或者 dependency injection(依赖注入) 的概念出现在JSF和EJB3以及很多所谓的“轻型容器”中。 这类容器大多注重于实现 stateless services(无状态服务) 的组件注射。 即便在支持对有状态的组件进行注射的情况下(例如JSF),事实上也难以用于处理应用程序状态, 因为有状态组件的范围难以有效而灵活地定义,并且属于更广范围的组件不能被注入到属于更窄范围的组件中。

Bijection(双向注入)和IoC的不同之处在于它是动态的、语境相关的以及双向的。 你可以把这一机制理解成将语境相关的变量(与当前线程绑定的各种上下文中的名称)对应到组件的属性中。 双向注入允许由容器对有状态的组件进行自动组装。它甚至允许组件可以安全而简单地处理上下文变量的值,只需要把它赋给组件的属性。

工作区管理(Workspace Management)和多窗口浏览

Seam应用程序让用户自由地在多个浏览器窗口中切换,每个窗口都与一个不同的、安全隔离的对话关联。 应用程序甚至可以利用 workspace management,允许用户在一个浏览器窗口的多个对话(工作区)之间进行切换。 Seam不仅提供正确的多窗口操作,还提供在一个窗口中模拟多个窗口的操作。

更喜欢XML注解

传统上,关于到底哪些元信息可以算作配置,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应用程序不只是服务HTML页面

当今的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 CaoXiaogangEchoYuLimin
1. tutorial.xml (1.1-1.4)130K20PseanchanJackyDigitalSonic
       (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

Chapter 1. Seam 入门

1.1. 试试看

本教程假定你已下载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 文件中。

1.1.1. 在JBoss AS上运行示例

第一步,确保已安装Ant,并正确设定了 $ANT_HOME$JAVA_HOME 的环境变量。接着在Seam的根目录下的 build.properties 文件中正确设定JBoss AS 4.0.5的安装路径。 若一切就绪,就可在JBoss的安装根目录下敲入 bin/run.shbin/run.bat 命令来启动JBoss AS。(译注:此外,请安装JDK1.5以上以便能直接运行示例代码)

现在只要在Seam安装目录 examples/registration 下输入 ant deploy 就可构建和部署示例了。

试着在浏览器中访问此链接:http://localhost:8080/seam-registration/

1.1.2. 在Tomcat服务器上运行示例

首先,确保已安装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容器环境中运行。

1.1.3. 运行测试

几乎所有的示例都有相应的TestNG的集成测试代码。最简便的运行测试代码的方法是在 examples/registration目录中运行 ant testexample。当然也可在IDE开发工具中使用TestNG插件来运行测试。

1.2. 第一个例子:注册示例

注册示例是个极其普通的应用,它可让新用户在数据库中保存自己的用户名,真实的姓名及密码。 此示例并不想一下子就把Seam的所有的酷功能全部秀出。然而, 它演示了EJB3 会话Bean作为JSF动作监听器及Seam的基本配置的使用方法。

或许你对EJB 3.0还不太熟悉,因此我们会对示例的慢慢深入说明。

此示例的首页显示了一个非常简单的表单,它有三个输入字段。试着在表单上填写内容并提交,一旦输入数据被提交后就会在数据库中保存一个user对象。

1.2.1. 了解代码

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

让我们看一下代码,就从最“底层”的实体Bean开始吧。

1.2.1.1. 实体Bean:User.java

我们需要EJB 实体Bean来保存用户数据。这个类通过注解声明性地定义了 persistencevalidation 属性。它也需要一些额外的注解来将这个类定义为Seam的组件。

Example 1.1. 

@Entity                                                                                  (1)
@Name("user")                                                                            (2)
@Scope(SESSION)                                                                          (3)
@Table(name="users")                                                                     (4)
public class User implements Serializable
{
   private static final long serialVersionUID = 1881413500711441951L;

   private String username;                                                              (5)
   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() {}                                                                      (6)

   @NotNull @Length(min=5, max=15)                                                       (7)
   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)                                                   (8)
   public String getUsername()
   {
      return username;
   }

   public void setUsername(String username)
   {
      this.username = username;
   }

}
(1)

EJB3标准注解 @Entity 表明了 User 类是个实体Bean.

(2)

Seam组件需要一个 组件名称,此名称由注解 @Name来指定。此名称必须在Seam应用内唯一。当JSF用一个与组件同名的名称去请求Seam来解析上下文变量, 且该上下文变量尚未定义(null)时,Seam就将实例化那个组件,并将新实例绑定给上下文变量。 在此例中,Seam将在JSF第一次遇到名为 user 的变量时实例化 User

(3)

每当Seam实例化一个组件时,它就将始化后的实例绑定给组件中 默认上下文 的上下文变量。默认的上下文由 @Scope注解指定。 User Bean是个会话作用域的组件。

(4)

EJB标准注解@Table 表明了将 User 类映射到 users 表上。

(5)

namepasswordusername 都是实体Bean的持久化属性。所有的持久化属性都定义了访问方法。当JSF渲染输出及更新模型值阶段时需要调用该组件的这些方法。

(6)

EJB和Seam都要求有空的构造器。

(7)

@NotNull@Length 注解是Hibernate Validator框架的组成部份, Seam集成了Hibernate Validator并让你用它来作为数据校验(尽管你可能并不使用Hibernate作为持久化层)。

(8)

标准EJB注解 @Id 表明了实体Bean的主键属性。

这个例子中最值得注意的是 @Name@Scope 注解,它们确立了这个类是Seam的组件。

接下来我们将看到 User 类字段在更新模型值阶段时直接被绑定给JSF组件并由JSF操作, 在此并不需要冗余的胶水代码来在JSP页面与实体Bean域模型间来回拷贝数据。

然而,实体Bean不应该进行事务管理或数据库访问。故此,我们无法将此组件作为JSF动作监听器,因而需要会话Bean。

1.2.1.2. 无状态会话Bean:RegisterAction.java

在Seam应用中大都采用会话Bean来作为JSF动作监听器(当然我们也可选择JavaBean)。

在我们的应用程序中确实存在一个JSF动作和一个会话Bean方法。在此示例中,只有一个JSF动作,并且我们使用会话Bean方法与之相关联并使用无状态Bean,这是由于所有与动作相关的状态都保存在 User Bean中。

这是示例中比较有趣的代码部份:

Example 1.2. 

@Stateless                                                                               (1)
@Name("register")
public class RegisterAction implements Register
{

   @In                                                                                   (2)
   private User user;

   @PersistenceContext                                                                   (3)
   private EntityManager em;

   @Logger                                                                               (4)
   private Log log;

   public String register()                                                              (5)
   {
      List existing = em.createQuery(
         "select username from User where username=#{user.username}")                    (6)
         .getResultList();

      if (existing.size()==0)
      {
         em.persist(user);
         log.info("Registered new user #{user.username}");                               (7)
         return "/registered.jsp";                                                       (8)
      }
      else
      {
         FacesMessages.instance().add("User #{user.username} already exists");           (9)
         return null;
      }
   }

}
(1)

EJB标准注解 @Stateless 将这个类标记为无状态的会话Bean。

(2)

注解 @In将Bean的一个属性标记为由Seam来注入。 在此例中,此属性由名为 user 的上下文变量注入(实例的变量名)。

(3)

EJB标准注解 @PersistenceContext 用来注入EJB实体管理器。

(4)

Seam的 @Logger 注解用来注入组件的 Log 实例。

(5)

动作监听器方法使用标准的EJB3 EntityManager API来与数据库交互,并返回JSF的输出结果。 请注意,由于这是个会话Bean,因此当 register() 方法被调用时事务就会自动开始,并在结束时提交(commit)。

(6)

请注意Seam让你在EJB-QL中使用JSF EL表达式。因此可在标准JPA Query 对象上调用普通的JPA setParameter() 方法,这样岂不妙哉?

(7)

Log API为显示模板化的日志消息提供了便利。

(8)

多个JSF动作监听器方法返回一个字符串值的输出,它决定了接下来应显示的页面内容。 空输出(或返回值为空的动作监听器方法)重新显示上一页的内容。 在普通的JSF中,用JSF的导航规则(navigation rule) 来决定输出结果的JSF视图id是很常用的。 这种间接性对于复杂的应用是非常有用的,值得去实践。但是,对于象示例这样简单的的应用,Seam让你使用JSF视图id作为输出结果,以减少对导航规则的需求。请注意,当你用视图id作为输出结果时,Seam总会执行一次浏览器的重定向。

(9)

Seam提供了大量的 内置组件(built-in components) 来协助解决那些经常遇到的问题。 用 FacesMessages 组件就可很容易地来显示模板化的错误或成功的消息。 内置的Seam组件还可由注入或通过调用 instance() 方法来获取。

这次我们并没有显式指定 @Scope,若没有显式指定时,每个Seam 组件类型就使用其默认的作用域。对于无状态的会话Bean, 其默认的作用域就是无状态的上下文。实际上 所有的 无状态的会话Bean都属于无状态的上下文。

会话Bean的动作监听器在此小应用中履行了业务和持久化逻辑。在更复杂的应用中,我们可能要将代码分层并重构持久化逻辑层成 专用数据存取组件,这很容易做到。但请注意Sean并不强制你在应用分层时使用某种特定的分层策略。

此外,也请注意我们的SessionBean会同步访问与web请求相关联的上下文(比如在 User 对象中的表单的值),状态会被保持在事务型的资源里(EntityManager 对象)。 这是对传统J2EE的体系结构的突破。再次说明,如果你习惯于传统J2EE的分层,也可以在你的Seam应用实行。但是对于许多的应用,这是明显的没有必要 。

1.2.1.3. 会话Bean的本地接口:Register.java

很自然,我们的会话Bean需要一个本地接口。

Example 1.3. 

@Local
public interface Register
{
   public String register();
}

所有的Java代码就这些了,现在去看一下部署描述文件。

1.2.1.4. Seam组件部署描述文件:components.xml

如果你此前曾接触过许多的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.initjndiPattern 属性。这里需要奇怪的@符号是因为ANT脚本会在部署应用时将正确的JNDI语法在标记处自动填补

1.2.1.5. Web部署描述文件:web.xml

我们将以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应用中的配置与此处的配置基本相同。

1.2.1.6. JSF配置:faces-config.xml

绝大多数的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代码中,所以我们甚至都不需要定义导航规则。

1.2.1.7. EJB部署描述文件:ejb-jar.xml

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>