第一章
第1章 seam 教程(1)
1.1 试例子
我们会话 bean动作接收器为我们小型的应用程序处理业务和持久化逻辑。在多数复杂的应用程序中,我们可能需要分层代码并重构持久化逻辑为一个专门的数据访问组件。那个做起来相当繁琐。但是,注意Seam 不强迫你陷入任何特殊的应用程序分层。
此外,注意我们的会话bean同时访问上下文相关的网页请求(在User对象中的表单值,对于例子来说),并且状态隐藏在事务资源中(实体管理器对象)。这 打破了传统的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 文件中,当你的项目到期时,逐渐产生出越来越多难以管理的XML文件。当你了解到Seam不要求应用程序组件伴随XML,你可能会安心。多数Seam应用程序要求很少量的XML,当你的项目变大时,它不会长得非常大。
不过,它常常是有用的,能为一些组件(尤其是Seam内建组件)提供一些额外的配置。在这里你用几个选项,在WEB-INF目录,以一个名为components.xml的文件提供这种几乎最灵活的选项配置。我们将用components.xml告诉Seam怎么用JNDI找到EJB组件。
Example 1.4.
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/
components-2.1.xsd">
<core:init jndi-pattern="@jndiPattern@"/>
</components>
这个代码配置了一个名为jndiPattern的属性,其与一个名为org.jboss.seam.core.init内建于Seam的组件相联。
有趣的@符号是放在这里,因为当我们部署应用程序时,我们的ANT 构建角本插当前的JNDI模块进来。
1.2.1.5. 网页部署描述: web.xml
我们的小应用程序的表示层将会被发布为一个WAR文件。所以我们将需要一个网页部署描述文件。
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>
<!-- JSF -->
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</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>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
web.xml 配置了Seam和 JSF。在这儿你看见的配置,在所有的Seam应用程序中几乎一样。
1.2.1.6. JSF配置: faces-config.xml
多数Seam应用程序用JSF视图作表示层。所以,常常我们会需要faces-config.xml。在我们的案例中,我们将用Facelets渲染出我们的视窗,因此,我们需要告诉JSF用Facelets作为它的模板引擎。
Example 1.6.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2"
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-facesconfig_1_2.xsd">
<!-- Facelets support -->
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>
注意,我们不需要任何JSF管理bean声明。我们的管理bean被注释为Seam组件。在Seam应用程序中, faces-config.xml被使用,常常不及在普通的JSF中。
实际上,一旦你设立了所有基础的描述文件, 当你增加新功能到Seam应用程序时你需要写的唯一XML是安排:导航控制或者jBPM处理定义。Seam处理程序流视图,并且配置数据是唯一真正地属于XML的事情。
在这个简单地例子里,我甚至于不需要导航控制,因为我们决定嵌视图id在我们动作代码中。
1.2.1.7. EJB 部署描述: ejb-jar.xml
ejb-jar.xml文件集成EJB3到Seam,通过糸SeamInterceptor(Seam拦截器)到这个文档的所有会话Bean。
<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>
1.2.1.8. EJB持久化部署描述: persistence.xml
persistence.xml文件告诉EJB持久提供者,在那儿找到数据源,并且包含一些卖主规范集。在这个案例里,在启动时能自动计划导入。
<?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>
1.2.1.9. 视图: register.xhtml 和 registered.xhtml
一个Seam应用程序的视图页能用任何支持JSF的技术来实现,在这个例子中,我们使用Facelets,因为我们认为它比JSP更好。
Example 1.7.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Register New User</title>
</head>
<body>
<f:view>
<h:form>
<s:validateAll>
<h:panelGrid columns="2">
Username: <h:inputText value="#{user.username}" required="true"/>
Real Name: <h:inputText value="#{user.name}" required="true"/>
Password: <h:inputSecret value="#{user.password}" required="true"/>
</h:panelGrid>
</s:validateAll>
<h:messages/>
<h:commandButton value="Register" action="#{register.register}"/>
</h:form>
</f:view>
</body>
</html>
在这里,唯一对Seam特殊的事情是<s:validateAll> 标签。这个JSF组件告诉JSF校验所有包含的输入字段,根据在实体bean指定的Hibernate校验器注释。
Example 1.8.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Successfully Registered New User</title>
</head>
<body>
<f:view>
Welcome, #{user.name}, you are successfully registered as #{user.username}.
</f:view>
</body>
</html>
这是一个使用内嵌EL的无趣的老式的Facelets 页面。这对Seam来说没有什么特别的东西。
1.2.1.10. EAR部署描述: application.xml
最后,因为我们的应用程序被发布为一个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>
<ejb>jboss-seam.jar</ejb>
</module>
<module>
<java>jboss-el.jar</java>
</module>
</application>
这个部署描述文件链接企业文档模块,并捆绑网页应用程序到上下文根 /seam-registration。
我们已经了解了整个应用程序中的所有文件!
1.2.2. 它怎样工作
当表单被提交,JSF请求Seam解析user变量。因为没有值已捆绑到那个名字(用任何Seam上下文),Seam实例化user组件,并且返回作为结果的User实体bean实例给JSF,然后存贮它在Seam会话上下文中。
现在,表单输入值根据 User实体的Hibernate校验器约束说明进行验证。假如违反约束,JSF重显示这个页面。否则,JSF捆绑表单输入值到User实体bean的属性。
下一步,JSF请求Seam解析register变量名。Seam在无状态上下文找到RegisterAction无状态会话bean ,并返回它。JSF调用register()接收器方法。
在继续调用前,Seam 拦截方法调用,并从Seam 会话上下文中注入User实体。
register()方法检查一个用户是否用了已经存的用户名。如果是,FacesMessages 组件会列出一个错误信息,并且一个空结果被返回,引起页面重新显示。FacesMessages组件插入JSF表达式嵌入到信息字符串,并增加一个JSF FacesMessage 到视图。
如果没有用户使用存在的用户名, "/registered.xhtml" 结果触发一个浏览器重定向到registered.xhtml 网页。当JSF进行渲染网页时,它请求Seam解析user变量,并使用来自Seam的会话范围的被返回的User实体的属性值。
1.3. 在seam里可点击的列表: 消息例子
可点击数据库搜索结果列表对任何在线应用程序是这样的重要,Seam在JSF的顶端提供专门的功能 使用EJB-QL
或 HQL进行查询变得容易 ,并用 JSF <h:dataTable>把结果作为一个可点击列表。消息例子演示了这种功能。
1.3.1. 理解代码
消息列表例子有一个名为 Message的实体bean,一个名为MessageListBean的会话bean,和一个JSP。
1.3.1.1. 实体bean: Message.java
Message实体定义了标题,文本,日期和时间讯息,以及一个用来标明是否消息已被读的标志:
Example 1.10.
@Entity
@Name("message")
@Scope(EVENT)
public class Message implements Serializable
{
private Long id;
private String title;
private String text;
private boolean read;
private Date datetime;
@Id @GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@NotNull @Length(max=100)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@NotNull @Lob
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@NotNull
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
@NotNull
@Basic @Temporal(TemporalType.TIMESTAMP)
public Date getDatetime() {
return datetime;
}
public void setDatetime(Date datetime) {
this.datetime = datetime;
}
}
1.3.1.2. 有状态会话bean: MessageManagerBean.java
正如前面的例子,我有一个会话bean:MessageManagerBean,它为我们的表单上的两上按钮定义动作接收器方法。一个按钮从列表选择消息,并显示消息。另外按钮删除消息。到目前为止,这与前面的例子几乎相同。
但是MessageManagerBean也负责在我们第一次导航到消息列表页面时取得消息列表。用户有各方法导航到这个页面,用 JSF 动作并不优先于所有这些方法,例如,用户可以使用标签。因此取得消息列表的工作发生在一个Seam工厂方法里,而不是在动作接收器方法中。
我们想在服务器请求之间缓存消息列表在内存,所以我们将使用一个有状态会话bean。
Example 1.11.
@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean implements Serializable, MessageManager
{
@DataModel // ⑴
private List<Message> messageList;
@DataModelSelection // ⑵
@Out(required=false) // ⑶
private Message message;
@PersistenceContext(type=EXTENDED) // ⑷
private EntityManager em;
@Factory("messageList") // ⑸
public void findMessages()
{
messageList = em.createQuery("from Message msg order by msg.datetime desc")
.getResultList();
}
public void select() // ⑹
{
message.setRead(true);
}
public void delete() // ⑺
{
messageList.remove(message);
em.remove(message);
message=null;
}
@Remove // ⑻
public void destroy() {}
}
⑴@DataModel注释,暴露了一个java.util.List 类属性给JSF页面,作为一个javax.faces.model.DataModel实例。
这允许我们使用JSF可点击链接到每行的<h:dataTable>列表。在这个案例中,DataModel用可用的会话上下文变量messageList构建。
⑵ @DataModelSelection注释,告诉 Seam注入相应被点击链接的列表元素。
⑶那么 ,@Out注释。直接暴露选择的值到页面。 所以,无论什么时候一个可点击列表被选择,Message实体被注入到有状态bean的属性里,并且随后反注到上下文变量message里.
⑷有状态bean有一个EJB3扩充持久化上下文。只要bean存在,在查询中取来的消息维持在被管状态,所以,任何后来的方法调用有状态bean能更新他们,不再需要对EntityManager做任何外在的调用了。
⑸我们第一次导航到JSP页面,messageList是没有值的。@Factory 注释,告诉Seam建立一个MessageManagerBean的实例,并调用findMessages() 方法初始化值。我们调用findMessages()一个工厂方法得到消息。
⑹ select()动作接收器方法 ,标记所选消息被读,并在数据库中更新它。
⑺delete()动作接收器方法 ,从数据库删除所选的消息。
⑻所有有状态bean Seam组件必须有一个标记为@Remove的无参方法 ,当Seam上下文结束时,Seam用它来移去有状态bean并清除任何服务边状态。
注意,这是一个会话范围Seam组件。它与用户注册会话相关联,并且来自于注册会话的所有请求共享一样的组件实例(在Seam应用程序中,我们常常保守地使用会话范围组件)。
1.3.1.3. 会话bean本地接口 MessageManager.java
当然,所有会话beans都一个
seam ----上下文组件
一个企业java框架
介绍
seam是一个企业java应用程序框架,它灵感源于下列原则:
一个“东西”
seam为你的应用程序中所有业务逻辑定义了一个统一的组件模式。一个seam组件可以是有状态的,用状态关联几个明确定义的上下文中的任意一个,包括长期运行、持久化、业务流程和“对话”上下文,那个状态在用户交互中被保持横跨多个网页请求。
在seam中表示层组件和业务逻辑层组件是没有区别的。你能够根据无论是那一种你设计的体系结构分层你的应用程序,这好于你现在用的无论那一种“大盖帽”框架组合,它们强迫你接受,硬使你的应用程序逻辑成为一个不自然的分层结构。
不象简单java ee或j2ee组件,seam组件可以同时访问与网页请求关联的状态和维持在事务资源的状态(不需要通过方法的参数手动传递网页请求状态)。你可以拒绝应用程序分层强迫你用老的j2ee平台是一个好事情。噢,没有东西阻止你用seam创建一个等价的层体系结构,不同在于你产生你自己的应用程序体系结构,并且决定层有那些,它们怎么一起工作。
用ejb3集成jsf
ejb3和jsf是java ee 5最新特色中的两个。ejb3被标记为服务器边业务和持久化逻辑新的组件模式。同是,jsf是一个主要的表示层组件模式。不幸地,两个组件模式都不能解决其自身计算的所有问题。实事上,ejb3和jsf最好一起用来工作。但是,java ee 5规范提供非标准的方法来集成两种组件模式。幸运的是,两种模式的创建者预见这种情况并且标准的扩充点,允许用其它框架来扩充和集成。
seam统一了ejb3和jsf两个组件模式,消除了粘合代码,让开发者思考业务问题。
写“万事”是一个EJB3的seam应用程序成为可能。假如你习惯地认为EJBs是粗粒度,号称“重量级”的对象,这可能会让你大吃一惊。然而,从开发者的角度来看,3.0版已完全改变了EJB的本性。一个EJB是一个细粒度对象,复杂性绝没有超过一个注释javabean。
另一方面,假如现在你不喜欢采用EJB3,你不必不得不采用它。实事上,任何java类可以是一个seam组件,并且,seam提供你所希望的一个“轻量级“容器的所有功能,并且,为任何组件,EJB或其它组件,还提供更多功能。
集成AJAX
seam支持最开源的JSF基础AJAX解决方案:jBoss RichFaces 和 ICEfaces.这些解决方案不需要写任何代码就可以增加AJAX能力到你的用户交互中去。
做为选择,seam提供了一个内建的JavaScript远程层,让你不需要一个中间活动层就能从客户边JavaScript异步调用组件。你甚至能订阅服务边JMS主题,而且能通过“AJAX推”接收信息。
这些方法两者都不会很好工作,如果不是因为seam的并发和状态管理,它确保多数并发细粒度、异步的AJAX请求在服务边被安全有效地处理。
业务流程作为一流构成物
可选地,seam通过jBPM提供透明的业务流程管理。你可能不愿相信,用seam和jBPM实现复杂的工作流、合作和任务管理是多么容易。
seam甚至于允许你用那个jBPM为业务流程定义的一样的语言(jPDL)来定义表示层页面流。
JSF为表示层提供了一个令人难以置信的富裕事件模式。seam通过精确的同样的事件处理机制,暴露jBPM的业务流程相关事件增加了这种模式,为seam的统一组件模式提供了统一的事件模式。
声明状态管理
我们使用的声明事务和声明安全的概念源于早期的EJB。EJB3引入了声明持久化上下文管理。他们是管理与特殊上下文相关联状态的主要问题的三个实例,同时他们确保当上下文结束是所有必需的清除工作发生。seam更多地使用声明状态管理,并且用它在应用程序状态。传统上,j2ee通过set与get servlet会话与请求属性,手动实现应用程序状态。这种方法对状态管理来说存在多种bug和内存漏洞源,当应用程序清除会话属性失败,或当在多窗全应用程序会话数据相关联的工作流冲突时。
声明状态管理由seam定义富裕的上下文模式促成的 。通过几个规范,请求、会话、应用和两个新上下文----对话和业务流程----从业务逻辑的视角点来看意味深长,扩充了上下文模式定义。
一旦你开始使用对话,你会吃惊于很多事是变得如此容易。你曾有在象Hibernate或 JPA类的ORM解决方案处理关联获取时遭遇的痛苦吗?seam的对话范围持化上下文意味着你将再不会看见“LazyInitializationException”。你曾有过刷新按钮的问题吗?后退按钮?用了重复表单提交?用了越过一个传递再改向的传播信息?seam的对话管理解决了这些问题,甚至不需要真正地考虑他们。从很久前的网页开始,他们就是打破了流行的状态管理体系结构的全部征兆。
双向注入
JSF和EJB3两个都存在控制倒置或依赖注入的概念,如在众多的所谓”轻量级容器“一样。那些容器的大部分强调实现无状态服务的组件注入。即使当有状态组件的注入被支持(如在JSF中),它实际上对处理应用程序状态是无用的,因为有状态组件的范围不能被充分的弹性的定义,并且因为属于广范围的组件不能被注入属于窄范围的组件。
双向注入不同于IoC(控制倒置),在于它是动态的,上下文的,双向的。你可以把它看作一种机制,用来对组件属性别名于上下文变量(在各种上下文中的名字捆绑到当前线程)。通过容器,双向注入允许有状态组件自动组合。它甚至允许一个组件安全、容易地操作一个上下文变量的值,只是把一个组件属性赋值给它就行了。
工作空间管理和多窗口游览
seam应用程序让用户自由地在多个游览器标签之间切换,每一个都关联着一个不同的、安全独立的“对话”。应用程序也可能有利于工作空间管理,允许用户用单一的游览器标签切换“对话”(工作空间)。seam不仅提供了恰当的多窗操作,而且也提供了单一的窗体中的多窗式操作。
宁愿用注释而不愿用XML
传统上,java社团一直深深地陷在关于用那种元信息作为配置更好的混乱中。j2ee和流行的“轻量级”容器都提供了基于XML的部署描述符,双方用于在不同的系统部署间产生真实地配置,也用于任何其它种类或者不易用java表示的声明中。java 5注释改变了这一切。
EJB3包含注释和“配置例外”作为最容易的方法,用一个声明的形式为容器提供信息。不幸的是,JSF严重依赖于冗长的XML配置文件。seam为声明状态管理和声明上下文划分扩充了EJB3提供的注释集合。这让你消除了JSF管理bean的嘈杂声并且减少了需要的XML,只留下真实地属于XML的的信息(JSF的导航控制)。
集成测试是容易的
seam组件是一个朴素的java类,是通过自然单元可测试的。但是对复杂的应用程序,单独的单元测试是不够充分的。对网页应用程序来说,传统的集成测试一直是一个杂乱困难的任务。因而,seam提供了seam应用程序的测试能力作为框架的的一个核心特色。你能容易写JUnit或者TestNG测试,重现与一个用户的整个交互活动,远离视图(JSP或Facelets页)地测验系统的整个组件。你能在你的IDE中直接运行这些测试,seam将自动用内嵌的JBoss部署EJB组件。
规范不是完美的
我们认为最近具体成形的j2ee是伟大的。但是我知道它决不是完美的。在规范中存在不足(如,限制了JSF生命周期得到GET请求),seam修补了这些不足。seam作者正与JCP(JAVA社区程序)专家一起工作,确定这些修补能返回到下一个标准版本中。
一个网页应用程序比服务html页有更多的内容
目前的网页框架考虑得太窄小。它们让你从用户表单得到输入并送进java对象,然后扔下你不管了。一个真正的网页应用程序框架应该从事下列问题:持久化、合作、异步、状态管理、安全、email、信息、PDF和图表产生、工作流、维客文本显示、网页服务、缓存等等。一旦你开始seam探索,你将会吃惊于很多问题是如此简单。
seam集成了JPA和Hibernate3实现持久化、EJB定时器服务和quartz实现轻量级异步、jBPM实现工作流、JBoss控制实现业务控制、Meldware Mail实现email、Hibernate搜索和Lucene实现全文本搜索、JMS实现通讯、JBoss缓存实现网页模型架缓存。seam建立了一个基于JAAS和JBoss控制之上的创新的安全框架控制层。甚至有JSF标签库来显示PDF,外发email,图表和维客文本。seam组件可以被作为一个网页服务异步调用,异步来自客户端JavaScript或者Google网页工具包,当然,也可直接来自JSF。
现在开始!
seam工作于任何j2ee应用程序服务器,并且甚至可工作于Tomcat(网页服务器)。如果你的工作环境支持EJB3,太好!如果它不支持,没问题,你能用seam内建成的事务管理使用JPA或Hibernate3实现持久化。或者,你能部署到内嵌在Tomcat中的JBoss,而得到对EJB3的完全支持。
结论是seam、JSF和EJB3的组合是用java开发复杂网页应用程序最简单的方法。你不要相信只需要很少的代码!
I
接口。
@Local
public interface MessageManager
{
public void findMessages();
public void select();
public void delete();
public void destroy();
}
From now on, we won't show local interfaces in our code examples.
Let's skip over components.xml, persistence.xml, web.xml, ejb-jar.xml, faces-config.xml
and application.xml since they are much the same as the previous example, and go straight
to the JSP.
从现在起,在我们的代码例子中不再显示本地接口了。
让我们跳过components.xml, persistence.xml, web.xml, ejb-jar.xml, faces-config.xml
and application.xml ,因为他们基本上与前面的例子一样。我们直接到JSP。
1.3.1.4. 视图: messages.jsp
JSP页面是一个JSF<h:dataTable> 组件的简单使用。此外,对Seam来说也什么特别的。
Example 1.12.
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Messages</title>
</head>
<body>
<f:view>
<h:form>
<h2>Message List</h2>
<h:outputText value="No messages to display"
rendered="#{messageList.rowCount==0}"/>
<h:dataTable var="msg" value="#{messageList}"
rendered="#{messageList.rowCount>0}">
<h:column>
<f:facet name="header">
<h:outputText value="Read"/>
</f:facet>
<h:selectBooleanCheckbox value="#{msg.read}" disabled="true"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Title"/>
</f:facet>
<h:commandLink value="#{msg.title}" action="#{messageManager.select}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Date/Time"/>
</f:facet>
<h:outputText value="#{msg.datetime}">
<f:convertDateTime type="both" dateStyle="medium" timeStyle="short"/>
</h:outputText>
</h:column>
<h:column>
<h:commandButton value="Delete" action="#{messageManager.delete}"/>
</h:column>
</h:dataTable>
<h3><h:outputText value="#{message.title}"/></h3>
<div><h:outputText value="#{message.text}"/></div>
</h:form>
</f:view>
</body>
</html>
1.3.2. 它怎样工作
我们第一次导航到messages.jsp 页面,无论是通过JSF 回发“postback” (faces请求)或者直接游览器GET请求(非faces请求),页面将试着解析messageList上下文变量。因为这个上下文变量是没有初始化,Seam将调用工厂方法findMessages(),根据数据库执行一个查询,并且导致DataModel 被反注入。这个DataModel为渲染 <h:dataTable>提供需要的行数据。
当用户点击<h:commandLink>,JSF调用select() 动作接收器。Seam拦截这个调用并注入所选的行数据到messageManager组件的message属性。动作接收器触发,标记所选消息被读。在调用结束,Seam反注入所选消息到上下文变量message。接下来,EJB 容器提交事务,并且把有变化的message被刷新到数据库。最后,页面被重新泻染,重显消息列表,并显示所选的消息在它的下面。
如果用户点<h:commandButton>,JSF 调用delete() 动作接收器。Seam拦截这个调用并注入所选的行数据到messageList组件的message属性。动作接收器触发,从列表中移去Message ,并调用EntityManager的remove() 方法。当调用结束,Seam重刷新messageList上下文变量,并清除上下文变量message。EJB 容器提交事务,并从数据库中删除Message。最后页面被重渲染,重显示消息列表。
1.4. seam和 jBPM: todo列表例子
jBPM 为工作流和任务管理提供尖瑞的功能。jBPM 怎样集成到Seam得到一个小的任务,我们用一个简单的"todo list"应用程序展示。因为管理任务列表是jBPM如此重要的核心功能,所以在这个例子中向乎没有任何java代码。
1.4.1. 理解代码
这个例子的中心是jBPM处理定义。也有两个JSP文件和两个普通的javaBeans(没有理由使用会话bean,因为他们不访问数据库,或者有任何其它的事务处理)。让我们开始处理定义:
Example 1.13. todo.jpdl.xml
<process-definition name="todo">
<start-state name="start"> // ⑴
<transition to="todo"/>
</start-state>
<task-node name="todo"> // ⑵
<task name="todo" description="#{todoList.description}"> // ⑶
<assignment actor-id="#{actor.id}"/> // ⑷
</task>
<transition to="done"/>
</task-node>
<end-state name="done"/> // ⑸
</process-definition>
⑴<start-state> 节点代表处理的逻辑开始。当处理开始,它马上转到todo节点。
⑵<task-node> 节点代表一个等到待状态,在这里
seam ----上下文组件
一个企业java框架
介绍
seam是一个企业java应用程序框架,它灵感源于下列原则:
一个“东西”
seam为你的应用程序中所有业务逻辑定义了一个统一的组件模式。一个seam组件可以是有状态的,用状态关联几个明确定义的上下文中的任意一个,包括长期运行、持久化、业务流程和“对话”上下文,那个状态在用户交互中被保持横跨多个网页请求。
在seam中表示层组件和业务逻辑层组件是没有区别的。你能够根据无论是那一种你设计的体系结构分层你的应用程序,这好于你现在用的无论那一种“大盖帽”框架组合,它们强迫你接受,硬使你的应用程序逻辑成为一个不自然的分层结构。
不象简单java ee或j2ee组件,seam组件可以同时访问与网页请求关联的状态和维持在事务资源的状态(不需要通过方法的参数手动传递网页请求状态)。你可以拒绝应用程序分层强迫你用老的j2ee平台是一个好事情。噢,没有东西阻止你用seam创建一个等价的层体系结构,不同在于你产生你自己的应用程序体系结构,并且决定层有那些,它们怎么一起工作。
用ejb3集成jsf
ejb3和jsf是java ee 5最新特色中的两个。ejb3被标记为服务器边业务和持久化逻辑新的组件模式。同是,jsf是一个主要的表示层组件模式。不幸地,两个组件模式都不能解决其自身计算的所有问题。实事上,ejb3和jsf最好一起用来工作。但是,java ee 5规范提供非标准的方法来集成两种组件模式。幸运的是,两种模式的创建者预见这种情况并且标准的扩充点,允许用其它框架来扩充和集成。
seam统一了ejb3和jsf两个组件模式,消除了粘合代码,让开发者思考业务问题。
写“万事”是一个EJB3的seam应用程序成为可能。假如你习惯地认为EJBs是粗粒度,号称“重量级”的对象,这可能会让你大吃一惊。然而,从开发者的角度来看,3.0版已完全改变了EJB的本性。一个EJB是一个细粒度对象,复杂性绝没有超过一个注释javabean。
另一方面,假如现在你不喜欢采用EJB3,你不必不得不采用它。实事上,任何java类可以是一个seam组件,并且,seam提供你所希望的一个“轻量级“容器的所有功能,并且,为任何组件,EJB或其它组件,还提供更多功能。
集成AJAX
seam支持最开源的JSF基础AJAX解决方案:jBoss RichFaces 和 ICEfaces.这些解决方案不需要写任何代码就可以增加AJAX能力到你的用户交互中去。
做为选择,seam提供了一个内建的JavaScript远程层,让你不需要一个中间活动层就能从客户边JavaScript异步调用组件。你甚至能订阅服务边JMS主题,而且能通过“AJAX推”接收信息。
这些方法两者都不会很好工作,如果不是因为seam的并发和状态管理,它确保多数并发细粒度、异步的AJAX请求在服务边被安全有效地处理。
业务流程作为一流构成物
可选地,seam通过jBPM提供透明的业务流程管理。你可能不愿相信,用seam和jBPM实现复杂的工作流、合作和任务管理是多么容易。
seam甚至于允许你用那个jBPM为业务流程定义的一样的语言(jPDL)来定义表示层页面流。
JSF为表示层提供了一个令人难以置信的富裕事件模式。seam通过精确的同样的事件处理机制,暴露jBPM的业务流程相关事件增加了这种模式,为seam的统一组件模式提供了统一的事件模式。
声明状态管理
我们使用的声明事务和声明安全的概念源于早期的EJB。EJB3引入了声明持久化上下文管理。他们是管理与特殊上下文相关联状态的主要问题的三个实例,同时他们确保当上下文结束是所有必需的清除工作发生。seam更多地使用声明状态管理,并且用它在应用程序状态。传统上,j2ee通过set与get servlet会话与请求属性,手动实现应用程序状态。这种方法对状态管理来说存在多种bug和内存漏洞源,当应用程序清除会话属性失败,或当在多窗全应用程序会话数据相关联的工作流冲突时。
声明状态管理由seam定义富裕的上下文模式促成的 。通过几个规范,请求、会话、应用和两个新上下文----对话和业务流程----从业务逻辑的视角点来看意味深长,扩充了上下文模式定义。
一旦你开始使用对话,你会吃惊于很多事是变得如此容易。你曾有在象Hibernate或 JPA类的ORM解决方案处理关联获取时遭遇的痛苦吗?seam的对话范围持化上下文意味着你将再不会看见“LazyInitializationException”。你曾有过刷新按钮的问题吗?后退按钮?用了重复表单提交?用了越过一个传递再改向的传播信息?seam的对话管理解决了这些问题,甚至不需要真正地考虑他们。从很久前的网页开始,他们就是打破了流行的状态管理体系结构的全部征兆。
双向注入
JSF和EJB3两个都存在控制倒置或依赖注入的概念,如在众多的所谓”轻量级容器“一样。那些容器的大部分强调实现无状态服务的组件注入。即使当有状态组件的注入被支持(如在JSF中),它实际上对处理应用程序状态是无用的,因为有状态组件的范围不能被充分的弹性的定义,并且因为属于广范围的组件不能被注入属于窄范围的组件。
双向注入不同于IoC(控制倒置),在于它是动态的,上下文的,双向的。你可以把它看作一种机制,用来对组件属性别名于上下文变量(在各种上下文中的名字捆绑到当前线程)。通过容器,双向注入允许有状态组件自动组合。它甚至允许一个组件安全、容易地操作一个上下文变量的值,只是把一个组件属性赋值给它就行了。
工作空间管理和多窗口游览
seam应用程序让用户自由地在多个游览器标签之间切换,每一个都关联着一个不同的、安全独立的“对话”。应用程序也可能有利于工作空间管理,允许用户用单一的游览器标签切换“对话”(工作空间)。seam不仅提供了恰当的多窗操作,而且也提供了单一的窗体中的多窗式操作。
宁愿用注释而不愿用XML
传统上,java社团一直深深地陷在关于用那种元信息作为配置更好的混乱中。j2ee和流行的“轻量级”容器都提供了基于XML的部署描述符,双方用于在不同的系统部署间产生真实地配置,也用于任何其它种类或者不易用java表示的声明中。java 5注释改变了这一切。
EJB3包含注释和“配置例外”作为最容易的方法,用一个声明的形式为容器提供信息。不幸的是,JSF严重依赖于冗长的XML配置文件。seam为声明状态管理和声明上下文划分扩充了EJB3提供的注释集合。这让你消除了JSF管理bean的嘈杂声并且减少了需要的XML,只留下真实地属于XML的的信息(JSF的导航控制)。
集成测试是容易的
seam组件是一个朴素的java类,是通过自然单元可测试的。但是对复杂的应用程序,单独的单元测试是不够充分的。对网页应用程序来说,传统的集成测试一直是一个杂乱困难的任务。因而,seam提供了seam应用程序的测试能力作为框架的的一个核心特色。你能容易写JUnit或者TestNG测试,重现与一个用户的整个交互活动,远离视图(JSP或Facelets页)地测验系统的整个组件。你能在你的IDE中直接运行这些测试,seam将自动用内嵌的JBoss部署EJB组件。
规范不是完美的
我们认为最近具体成形的j2ee是伟大的。但是我知道它决不是完美的。在规范中存在不足(如,限制了JSF生命周期得到GET请求),seam修补了这些不足。seam作者正与JCP(JAVA社区程序)专家一起工作,确定这些修补能返回到下一个标准版本中。
一个网页应用程序比服务html页有更多的内容
目前的网页框架考虑得太窄小。它们让你从用户表单得到输入并送进java对象,然后扔下你不管了。一个真正的网页应用程序框架应该从事下列问题:持久化、合作、异步、状态管理、安全、email、信息、PDF和图表产生、工作流、维客文本显示、网页服务、缓存等等。一旦你开始seam探索,你将会吃惊于很多问题是如此简单。
seam集成了JPA和Hibernate3实现持久化、EJB定时器服务和quartz实现轻量级异步、jBPM实现工作流、JBoss控制实现业务控制、Meldware Mail实现email、Hibernate搜索和Lucene实现全文本搜索、JMS实现通讯、JBoss缓存实现网页模型架缓存。seam建立了一个基于JAAS和JBoss控制之上的创新的安全框架控制层。甚至有JSF标签库来显示PDF,外发email,图表和维客文本。seam组件可以被作为一个网页服务异步调用,异步来自客户端JavaScript或者Google网页工具包,当然,也可直接来自JSF。
现在开始!
seam工作于任何j2ee应用程序服务器,并且甚至可工作于Tomcat(网页服务器)。如果你的工作环境支持EJB3,太好!如果它不支持,没问题,你能用seam内建成的事务管理使用JPA或Hibernate3实现持久化。或者,你能部署到内嵌在Tomcat中的JBoss,而得到对EJB3的完全支持。
结论是seam、JSF和EJB3的组合是用java开发复杂网页应用程序最简单的方法。你不要相信只需要很少的代码!
I
处理执行暂停,等待一个或多个任务执行。
⑶<task>元素定义一个被用户执行的任务。因为,在这个节点只定义了一个任务,当它完成,执行恢复,并且我们转到结束状态。这个任务从Seam组件todoList(JavaBeans中的一个)得到它的描述。
⑷当他们被建立时,任务需要被指派给一个用户或一组用户。在这个案例里,任务被指派给当前用户,我们从一个内建的Seam组件actor得到它。任何Seam组件可能习惯于执行任务委派
⑸<end-state> 节点定义业务处理的逻辑结束。当执行到这个节点,处理实例被摧毁。
假若我们用JBossIDE提供的处理定义编辑器观察这个定