`

JBoss Seam的事件机制 (2)

    博客分类:
  • Seam
阅读更多
JBoss Seam如何做到松耦合的架构呢?光有具备上下文的组件模型是不够的。还必须有:

事件模型。事件模型通过事件<-->监听器模式来构建。采用的方式是象JSF方法绑定一样的方式实现。
使用注释和拦截器来纵向切入实现业务逻辑的组件,从而达到松耦合的事件触发和响应
JBoss Seam的组件模型本身就是为事件驱动的应用来设计的。这些事件都是通过JSF的表达式语言的方法绑定来映射的。在JBoss Seam中,事件可以分为:

JSF事件,例如:<h:commandButton value="Click me!" action="#{helloWorld.sayHello}"/> JSF的按钮绑定动作

jBPM状态转移事件,例如:<start-page name="hello" view-id="/hello.jsp">    <transition to="hello">        <action expression="#{helloWorld.sayHello}"/>    </transition></start-page> jBPM流程或页面流定义

Seam页面动作,页面事件发生在我们渲染一个页面之前,我们通过在WEB-INF/pages.xml文件中配置页面动作。我们可以为一个特定的JSF视图ID (View ID)设定一个页面动作,例如:<pages>    <page view-id="/hello/hello.jsp" action="#{helloWorld.sayHello1}"/></pages>
我们也可以使用通配符为一个页面模式设定页面动作,例如:
<pages>    <page view-id="/hello/*" action="#{helloWorld.sayHello2}"/></pages>
如果多个页面动作匹配当前的页面视图的话,那么所有动作按照最窄到最宽泛的顺序依次调用
例如上述sayHello1,sayHello2动作都和hello.jsp匹配,那么在hello.jsp页面上先调用
sayHello1然后调用sayHello2。页面动作可以返回一个JSF的输出outcome,如果outcome
不为null的话,Seam将使用定义好的规则去导向到一个视图。更棒的是page元素中的视图
view-id可以不是JSP页面或者Facelet页面,允许我们通过view-id来整合基于动作的Struts
或者Webwork框架。你可以使用action元素指定多个条件页面动作(在某种条件下才执行的动作)
例如:
<pages>    <page view-id="/hello.jsp">        <action execute="#{helloWorld.sayHello}" if="#{not validation.failed}"/>        <action execute="#{hitCount.increment}"/>    </page></pages>


Seam组件驱动的事件,Seam组件之间可以直接通过方法调用来相互沟通。有状态的组件可以实现观察者模式。虽然如此,Seam提供的组件驱动的事件比直接通过方法调用更加松耦合。我们来看一下如何设置组件驱动的事件。我们可以在components.xml文件中设置事件监听器(观察者):<components>    <event type="hello"> // 设置事件, Observable        <action execute="#{helloListener.sayHelloBack}"/> // 设置监听器,Observer        <action execute="#{logger.logHello}"/> // 设置监听器,Observer    </event></components> 你可能想问事件类型type="hello"是什么,它只是一个任意的字串。事件类型将在组件驱动事件的时候使用
(raiseEvent)。当事件发生时,在事件中注册的动作将根据在components.xml文件中的次序依次调用。你可能要问:我如何
在组件中触发一个事件呢?基本上,你有两种选择:使用内置的组件和通过注释。下列代码在运行HelloWord的sayHello方法时
触发事件类型为"hello"的事件(在components.xml文件中配置):
@Name("helloWorld")public class HelloWorld {    public void sayHello() {        FacesMessages.instance().add("Hello World!");        Events.instance().raiseEvent("hello"); // Events 内置组件 触发hello事件    }}
@Name("helloWorld")public class HelloWorld {    @RaiseEvent("hello") // 注释 触发hello事件    public void sayHello() {        FacesMessages.instance().add("Hello World!");    }}
我们看到HelloWorld的sayHello方法其实是事件的产生者。值得注意的是事件的产生者和事件的消费者
(监听者)没有任何依赖性,例如下面的hello监听器和上述的HelloWord没有依赖:
@Name("helloListener") // 在components.xml文件的hello事件中注册的监听者public class HelloListener {    public void sayHelloBack() { // 当hello事件触发后,监听者调用该监听方法        FacesMessages.instance().add("Hello to you too!");    }}
注意:如果你讨厌在components.xml文件配置太多事件和监听器,那么你也可以使用注释来配置:
@Name("helloListener")public class HelloListener {    @Observer("hello") // 指明使用sayHelloBack方法来监听hello事件    public void sayHelloBack() {        FacesMessages.instance().add("Hello to you too!");    }}
到这里你会发现我们根本没有用事件对象!实际上,事件产生者和事件消费者之间根本不必有事件对象来
处理状态的问题。状态问题由Seam的上下文处理了,并且在Seam组件之间共享!
不过,你要真的想要一个事件对象,可以象这样:
@Name("helloWorld") // 事件产生者public class HelloWorld {    private String name;    public void sayHello() {        FacesMessages.instance().add("Hello World, my name is #0.", name);        Events.instance().raiseEvent("hello", name); // name是String参数,传递多个Object参数    }}
@Name("helloListener") // 事件监听者public class HelloListener {    @Observer("hello")    public void sayHelloBack(String name) {        FacesMessages.instance().add("Hello #0!", name);    }}

Seam具有上下文的事件,Seam本身定义了一系列内置的事件。注意这些事件的名称都是字符串定义的:
          Seam组件可以监听(观察)这些事件,就像他们观察组件驱动的事件一样。我们将在别的文章中专门讲述。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics