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对象。

registration.png

1.2.1. 了解代码

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

register.png

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

1.2.1.1. 实体Bean:User.java

我们需要EJB 实体Bean来保存用户数据。这个类通过注解声明性地定义了 persistencevalidation 属性。它也需要一些额外的注解来将这个类定义为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;
   }

}
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                                                                               
@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;
      }
   }

}
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. 

 
    

此代码配置了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"?>


    <!-- Seam --&gt

    org.jboss.seam.servlet.SeamListener

    <!-- MyFaces --&gt

    
            org.apache.myfaces.webapp.StartupServletContextListener
        javax.faces.STATE_SAVING_METHODclientFaces Servletjavax.faces.webapp.FacesServlet1

    <!-- Faces Servlet Mapping --&gt
    Faces Servlet*.seam

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"?>



    <!-- A phase listener is needed by all Seam applications --&gt

    org.jboss.seam.jsf.SeamPhaseListener

注意我们不需要申明任何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的整合。

org.jboss.seam.ejb.SeamInterceptor*org.jboss.seam.ejb.SeamInterceptor
1.2.1.8. EJB持久化部署描述文件:persistence.xml

persistence.xml 文件告诉EJB的持久化层在哪找到数据源,该文件也含有一些厂商特定的设定。此例在程序启动时自动创建数据库Schema。

<?xml version="1.0" encoding="UTF-8"?>
org.hibernate.ejb.HibernatePersistencejava:/DefaultDS
1.2.1.9. 视图:register.xhtmlregistered.xhtml

对于Seam应用的视图可由任意支持JSF的技术来实现。在此例中,我们使用了JSP,因为大多数的开发人员都很熟悉, 且这里并没有其它太多的要求。(我们建议你在实际开发中使用Facelets)。

Example 1.7. 


 
  Register New User
     
Username
Real Name
Password

这里的 标签是Seam特有的。 该JSF组件告诉JSF让它用实体Bean中所指定的Hibernat验证器注解来验证所有包含输入的字段。

Example 1.8. 

 
  Successfully Registered New User
    Welcome, ,
    you are successfully registered as .
  

这是个极其普通的使用JSF组件的JSP页面,与Seam毫无相干。

1.2.1.10. EAR部署描述文件:application.xml

最后,因为我们的应用是要部署成EAR的,因此我们也需要部署描述文件。

Example 1.9. 

<?xml version="1.0" encoding="UTF-8"?>
Seam Registrationjboss-seam-registration.war/seam-registrationjboss-seam-registration.jarjboss-seam.jarel-api.jarel-ri.jar

此部署描述文件联接了EAR中的所有模块,并把Web应用绑定到此应用的首页 /seam-registration

至此,我们了解了整个应用中 所有的 部署描述文件!

1.2.2. 工作原理

当提交表单时,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 的变量,并使用从Seam会话作用域返回的 User 实体的属性值。

1.3. Seam中的可点击列表:消息示例

在几乎所有的在线应用中都免不了将搜索结果显示成可点击的列表。 因此Sean在JSF层之上提供了特殊的功能,使得我们很容易用EJB-QL或HQL来查询数据并用JSF 将查询结果显示成可点击的列表。我们将在接下的例子中演示这一功能。

messages.png

1.3.1. 理解代码

此消息示例中有一个实体Bean,Message,一个会话Bean MessageListBean 及一个JSP页面。

1.3.1.1. 实体Bean:Message.java

Message 实体定义了消息的title,text,date和time以及该消息是否已读的标志:

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 会话Bean也负责抓取消息列表,考虑到用户可能以多种方式来浏览该页面,他们也有可能不是由JSF动作来完成,比如用户可能将该页加入收藏夹。 因此抓取消息列表发生在Seam的工厂方法中,而不是在动作监听器方法中。

之所以将此会话Bean设为有状态的,是因为我们想在不同的服务器请求间缓存此消息列表。

Example 1.11. 

@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean implements Serializable, MessageManager
{

   @DataModel                                                                            
   private List 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 @Destroy                                                                      
   public void destroy() {}

}
1

注解 @DataModel 暴露了 java.util.List 类型的属性给JSF页面来作为 javax.faces.model.DataModel 的实例。 这允许我们在JSF 的每一行中能使用可点击列表。在此例中,DataModel 可在变量名为 messageList 的会话上下文中被使用。

2

@DataModelSelection 注解告诉了Seam来注入 List 元素到相应的被点击链接。

3

注解 @Out 直接暴露了被选中的值给页面。 这样一来,每次可点击列表一旦被选中,Message 就被会注入给有状态Bean的属性,紧接着 向外注入(outjected)给变量名为 message 的事件上下文的属性。

4

此有状态Bean有个EJB3的 扩展持久化上下文(extended persistence context)。只要Bean存在,查询中获取的消息就会保留在受管理的状态中。 这样一来,此后对有状态Bean的所有方法调用勿需显式调用 EntityManager 就可更新这些消息了。

5

当我们第一次浏览JSP页面时,messageList 上下文变量尚未被初始化,@Factory 注解告诉Seam来创建 MessageManagerBean 的实例并调用 findMessages() 方法来初始化上下文变量。 我们把 findMessages() 当作 messages工厂方法

6

select() 将选中的 Message 标为已读,并同时更新数据库。

7

delete() 动作监听器方法将选中的 Message 从数据库中删除。

8

对于每个有状态的会话Bean,Seam组件的所有方法中 必须 有一不带参数的方法被标为 @Remove @Destroy 以确保在Seam的上下文结束时删除有状态Bean,并同时清除所有服务器端的状态。

请注意,这是个会话作用域的Seam组件。它与用户登入会话相关联,并且登入会话的所有请求共享同一个组件的实例。 (在Seam的应用中,我们通常使用会话作用域的组件。)

1.3.1.3. 会话Bean的本地接口:MessageManager.java

当然,每个会话Bean都有个业务接口。

@Local
public interface MessageManager
{
   public void findMessages();
   public void select();
   public void delete();
   public void destroy();
}

从现在起,我们在示例代码中将不再对本地接口作特别的说明。

由于XML文件与此前的示例几乎都一样,因此我们略过了 components.xmlpersistence.xmlweb.xmlejb-jar.xmlfaces-config.xmlapplication.xml 的细节,直接来看一下JSP。

1.3.1.4. 视图:messages.jsp

JSP页面就是直接使用JSF 的组件,并没有与Seam有什么关系。

Example 1.12. 

 
  Messages
     

Message List

1.3.2. 工作原理

当我们首次浏览 messages.jsp 页面时,无论是否由回传(postback)的JSF(页面请求)或浏览器直接的GET请求(非页面请求),此JSP页面将设法解析 messagelist 上下文变量。 由于上下文变量尚未被初始化,因此Seam将调用工厂方法 findmessages(),该方法执行了一次数据库查询并导致 DataModel 被向外注入。 DataModel 提供了渲染 所需的行数据。

当用户点击 时,JSF就调用 Select() 动作监听器。 Seam拦截此调用并将所选行的数据注入给 messageManager 组件的 message 属性。 而动作监听器将所选定的 Message 标为已读。在此调用结束时,Seam向外注入所选定的 Message 给名为 message 的变量。 接着,EJB容器提交事务,将 Message 的已读标记写入数据库。 最后,该网页重新渲染,再次显示消息列表,并在列表下方显示所选消息的内容。

如果用户点击了 ,JSF就调用 delete() 动作监听器。 Seam拦截此调用并将所选行的数据注入给 messageManager 组件的 message 属性。 触发动作监听器,将选定的 Message 从列表中删除并同时在 EntityManager 中调用 remove() 方法。在此调用的最后,Seam刷新 messageList 上下文变量并清除名为 message 的上下文变量。 接着,EJB容器提交事务,将 Message 从数据库中删除。最后,该网页重新渲染,再次显示消息列表。

1.4. Seam和jBPM:待办事项列表(todo list)示例

jBPM提供了先进的工作流程和任务管理的功能。为了体验一下jBPM是如何与Seam集成在一起工作的,在此将给你一个简单的管理“待办事项列表”的应用。由于管理任务列表等功能是jBPM的核心功能,所以在此例中只用了很少的Java代码。

todo.png

1.4.1. 理解代码

这个例子的核心是jBPM的流程定义(process definition)。此外,还有两个JSP页面和两个简单的JavaBeans(由于他们不用访问数据库,或有其它事务相关的行为,因此并没有用会话Bean)。让我们先从流程定义开始:

Example 1.13. 


      
      
         
      

1

节点 代表流程的逻辑开始。一旦流程开始时,它就立即转入 todo节点。

2

节点代表 等待状态,就是在执行业务流程暂停时,等待一个或多个未完成的任务。

3

元素定义了用户需要完成的任务。 由于在这个节点只有定义了一个任务,当它完成,或恢复执行时我们就转入结束状态。 此任务从Seam中名为 todolist 的组件(JavaBeans之一)获得任务description。

4

任务在创建时就会被分配给一个用户或一组用户时。在此示例中,任务是分配给当前用户,该用户从一个内置的名为 actor 的Seam组件中获得。任何Seam组件都可用来执行任务指派。

5

节点定义业务流程的逻辑结束。当执行到达这个节点时,流程实例就要被销毁。

如果我们用jBossIDE所提供的流程定义编辑器来查看此流程定义,那它就会是这样:

todo-process.png

这个文档将我们的 业务流程 定义成节点图。这可能是最常见的业务流程:只有一个 任务 被执行,当这项任务完成之后,业务流程就结束了。

第一个JavaBean处理登入界面 login.jsp。 它的工作就是用 actor 组件初始化jBPM用户id(在实际的应用中,它也需要验证用户。)

Example 1.14. 

@Name("login")
public class Login {

   @In
   private Actor actor;

   private String user;

   public String getUser() {
      return user;
   }

   public void setUser(String user) {
      this.user = user;
   }

   public String login()
   {
      actor.setId(user);
      return "/todo.jsp";
   }
}

在此我们使用了 @In 来将actor属性值注入到Seam内置的 Actor 组件。

JSP页面本身并没有什么特别之处:

Example 1.15. 


Login

Login

第二个JavaBean负责启动业务流程实例及结束任务。

Example 1.16. 

@Name("todoList")
public class TodoList {

   private String description;

   public String getDescription()                                                        
   {
      return description;
   }

   public void setDescription(String description) {
      this.description = description;
   }

   @CreateProcess(definition="todo")                                                     
   public void createTodo() {}

   @StartTask @EndTask                                                                   
   public void done() {}

}
1

description属性从JSP页接受用户输入,并将它暴露给流程定义,这样就可让Seam来设定任务的descrption。

2

Seam的 @CreateProcess 注解为指定名称的流程定义创建了一个新的jBPM流程实例。

3

Seam的 @StartTask 注解用来启动任务,@EndTask 用来结束任务,并允许恢复执行业务流程。

在实际的应用中,@StartTask@EndTask 不会出现在同一个方法中,因为为了完成任务,通常用应用中有许多工作要做。

最后,该应用的主要内容在 todo.jsp 中:

Example 1.17. 



Todo List

Todo List

让我们对此逐一加以说明。

该JSP页面将从Seam内置组件 taskInstanceList 获得的任务渲染成任务列表,此列表在JSF表单内被定义。

 
   
...

列表中的每个元素就是一个jBPM类 taskinstance 的实例。 以下代码简单地展示了列表中每一任务的有趣特性。为了让用户能更改description、priority及due date的值,我们使用了输入控件。

 
   

该按钮通过调用被注解为 @StartTask @EndTask 的动作方法来结束任务。它把任务id作为请求参数传给Seam:

 
   

(请注意,这是在使用Seam seam-ui.jar 包中的JSF 控件。)

这个按钮是用来更新任务属性。当提交表单时,Seam和jBPM将直接更改任务的持久化,不需要任何的动作监听器方法:

 
   

第二个表单通过调用注解为 @CreateProcess的动作方法来创建新的项目(item)。

 
   

这个例子还需要另外几个文件,但它们只是标准的jBPM和Seam配置并不是很有趣。

1.4.2. 工作原理

待完成

1.5. Seam页面流:猜数字范例

对有相对自由(特别)导航的Seam应用程序而言,JSF/Seam导航规则是定义页面流的一个完美的方法。 而对于那些带有更多约束的导航,特别是带状态的用户界面而言,导航规则反而使得系统流程变得难以理解。 要理解整个流程,你需要从视图页面、动作和导航规则里一点点把它拼出来。

Seam允许你使用一个jPDL流程定义来定义页面流。下面这个简单的猜数字范例将演示这一切是如何实现的。

numberguess.png

1.5.1. 理解代码

这个例子由一个JavaBean、三个JSP页面和一个jPDL页面流定义组成。让我们从页面流开始:

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13270562/viewspace-244089/,如需转载,请注明出处,否则将追究法律责任。

下一篇: 用Seam-gen起步
user_pic_default.png
请登录后发表评论 登录
全部评论
<%=items[i].createtime%>

<%=items[i].content%>

<%if(items[i].items.items.length) { %>
<%for(var j=0;j
<%=items[i].items.items[j].createtime%> 回复

<%=items[i].items.items[j].username%>   回复   <%=items[i].items.items[j].tousername%><%=items[i].items.items[j].content%>

<%}%> <%if(items[i].items.total > 5) { %>
还有<%=items[i].items.total-5%>条评论 ) data-count=1 data-flag=true>点击查看
<%}%>
<%}%>
<%}%>

转载于:http://blog.itpub.net/13270562/viewspace-244089/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值