一个简单的例子

我们通过一个简单的例子,来介绍消息总线的使用方法。

假设这样一种场景:某公司的一位程序员进行费用申请,他申请的费用会经过所属项目组的项目经理的审批。 如果转换成消息模型那就是:“申请费用”是一个事件,而项目经理对此感兴趣。

为了让我们的视线关注于事件本身,程序代码我们尽可能的精简:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
  renderKitId="AJAX">
  <w:page title="apply fee">
    <w:form>
      <layout:panelGrid columns="2">
        <w:textField id="money"></w:textField>
        <w:button id="apply" />
      </layout:panelGrid>
    </w:form>
  </w:page>
</f:view>

后台的 ApplyBean 代码片断如下:

@ManagedBean(name="applyBean", scope=ManagedBeanScope.SESSION)
public class ApplyBean {

  @Bind
  private int money = 100;

  @Action
  public void apply(){
    System.out.println("I'm a programmer, I apply " + money + " money");
  }
}

基于 IoVC 原理,整个页面即可运行,效果如下:

消息总线的简单用法

现在需要解决的问题是:当程序员调用费用申请的方法时,要能够让项目经理知道这件事情。我们做一个新的托管Bean:ProjectManagerBean,代码如下:

@ManagedBean(name="projectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {

  private void applyFeeListener(int money) {
    System.out.println("I'm your project manager, you apply " + money + " money.");
  }
}

那么,我们如何通过事件机制,让这apply()与 applyFeeListener(int money)两个方法关联起来呢?

首先是发送事件,我们将代码修订如下:

@Action
1@RaiseEvent("applyFee(this.money)")
public void apply(){
  System.out.println("I'm a programmer, I apply " + money + " money");
}

请注意,我们增加了一个@RaiseEvent 的annotation,它的参数中:“applyFee”代表事件类型,“this.money”代表传递的参数。

ProjectManagerBean是怎样注册成为Listener的呢?

1@EventListener("applyFee")
private void applyFeeListener(int money) {
  System.out.println("I'm your project manager, you apply " + money + " money.");
}

上述代码含义就是:通过@EventListener这个annotation标明:applyFeeListener方法是个事件监听器方法,并且, 它所感兴趣的事件类型是:applyFee。

好了,我们来看一下程序运行的输出结果:

2008-03-01 14:57:30 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 14:57:30 信息 [con.out] I'm your project manager, you apply 100 money.

两者之间已经通过事件关联起来了。

现在我们需要扩展一下:除了项目经理感兴趣,部门经理对此事也感兴趣,那么,我们根本无需更改任何代码,只是新增一个新的 DepartManagerBean:

@ManagedBean(name="departManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {

  @EventListener("applyFee")
  private void applyFeeListener(int money) {
    System.out.println("I'm your department manager, you apply " + money + " money.");
  }
}

程序运行的输出结果如下:

2008-03-01 15:01:03 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 15:01:03 信息 [con.out] I'm your department manager, you apply 100 money.
2008-03-01 15:01:03 信息 [con.out] I'm your project manager, you apply 100 money.

EventBroadcaster

程序员申请费用,现在项目经理和部门经理都通过事件机制知道此事了。我们希望在同一个方法中,对事件进行分类: 如果费用小于 等于100,那么,就发送一个 applyLittleFee 的事件,如果费用大于100,则发送一个 applyMuchFee 的事件;项目经理关注 applyLittleFee 事件,而部门经理关注 applyMuchFee 的事件。ApplyBean修订如下:

public class ApplyBean {
  @Bind
  private int money = 100;

  @Inject
  1private EventBroadcaster event;

  @Action	
  public void apply(){
    System.out.println("I'm a programmer, I apply " + money + " money");
    2if(money <=100) event.broadcast(this, "applyLittleFee", money);
    else event.broadcast(this, "applyMuchFee", money);
  }
}

此处的 EventBroadcaster (事件发送对象) 被注入,通过此对象,你对“发送什么事件”、“何时发送事件”都能够进行详细的控制。

ProjectManagerBean与DepartManagerBean修订如下:

@ManagedBean(name="projectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {

  1@EventListener("applyLittleFee")
  private void applyFeeListener(int money) {
    System.out.println("I'm your project manager, you apply " + money + " money.");
  }
}
@ManagedBean(name="departManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {

  1@EventListener("applyMuchFee")
  private void applyFeeListener(int money) {
    System.out.println("I'm your department manager, you apply " + money + " money.");
  }
}

请注意,ProjectManagerBean及DepartManagerBean所感兴趣的事件也做了相应的修订,程序运行输出如下:

2008-03-01 15:10:10 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 15:10:11 信息 [con.out] I'm your project manager, you apply 100 money.
2008-03-01 15:10:14 信息 [con.out] I'm a programmer, I apply 1000 money
2008-03-01 15:10:14 信息 [con.out] I'm your department manager, you apply 1000 money.

基于事件的导航

有时候会有这样一种需求:程序员申请费用,根据费用的大小,要自动导航到不同的页面。但有时候, 要重定向到哪些页面我们事先并不清楚。以上述场景为例:当项目经理审批时,希望导航到 projectManager.xhtml页面, 当部门经理审批时,希望导航到 departManager.xhtml页面。在 OperaMasks 2.0中,如何解决此需求?

我们首先完成这两个页面:

projectManager.xhtml:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
  renderKitId="AJAX" xmlns:h="http://java.sun.com/jsf/html">
  <w:page title="project manager">
    I'm your project manager, you apply <h:outputText id="money"/> money.
  </w:page>
</f:view>

departManager.xhtml

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
  renderKitId="AJAX" xmlns:h="http://java.sun.com/jsf/html">
  <w:page title="project manager">
    I'm your depart manager, you apply  <h:outputText id="money"/> money.
  </w:page>
</f:view>

再对 ProjectManagerBean及DepartManagerBean修订如下:

@ManagedBean(name="projectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {

  @Bind
  private int money;
  
  @EventListener("applyLittleFee")
  private String applyFeeListener(int money) {
    this.money = money;
    1return "view:projectManager";
  }
}
@ManagedBean(name="DepartManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {

  @Bind
  private int money;

  @EventListener("applyMuchFee")
  private String applyFeeListener(int money) {
    this.money = money;
    1return "view:departManager";
  }
}

请注意观察:return "view:projectManager"语句中,前面的 view 意即告诉OperaMasks,这是要导航,后面的projectManager代表 view 的ID。

程序运行效果如下:

问题:如果一个事件有多个Listener,并且每个Listener都会返回“view:somePage",情况会如何?答案是:以第一个返回结果为准。

其它特性

我们只是通过上述例子了解了OperaMasks 2.0中模型事件的一些小特性,事实上,OperaMasks 2.0中模型事件是比较完备的,还有许多其它特性我们并没有介绍,譬如,我们可以在 Action 中直接通过return 语句触发事件:

return "#applyFee(this.money)";

在 @RaiseEvent中,我们还可以引用传递给方法的参数:

@RaiseEvent("showResponse($1.name)")
public void loginSuccess(User user) {
...
}
[上一页] [下一页]