Symfony2 EventDispatcher组件

一个插件系统中,A插件在不影响其它插件的前提下,添加新的方法,或者在一个方法运行前做一些准备工作,通过继承来实现扩展是很不容易的,由于插件之间的关联关系,A插件的改变也会使得关联的插件被动的修改。
Symfony2的EventDispatcher组件实现了中介者(mediator)模式,实现了插件之间的解耦和关联的关系。
举个栗子,在HttpKernel组件中,一旦Response被创建了,在Response发送给客户端的时候,允许系统的其它模块修改它是很有必要和很有用的(例如:在头部添加缓存字段)。Symfony2内核调度kernel.response事件,那么监听该事件的监听器就可以修改Response对象了:
a) 监听器(PHP对象)告诉一个特定的dispatcher类,它要监听kernel.response事件;
b) 在某一时刻,Symfony就会告诉dispatcher调度kernel.response事件,把一个包含Response对象的Event对象传入事件调度方法EventDispatcher::dispatch()
c) dispatcher通知所有监听kernel.response事件的监听器运行,允许这些监听器修改Response对象。
使用方法
Events
当一个事件被调度,该事件会有一个唯一的名称(例如:kernel.response),可以被任意多个监听器监听。同时实例化一个Event对象,并传入到所有的监听器中。在后面会看到,Event对象包含了被调度事件的数据。
命名规范
事件名可以是任意的字符串,但是可以遵循以下几点命名规范:
* 只使用小写字符、数字、点、下划线;
* 前缀名使用带有点的命名;
* 结束部分的名字使用一个能表达当前事件行为的动词;
事件名和事件对象
dispatcher调度事件通知监听器时候,会把一个Event对象作为参数传入到监听器中。基类的Event非常简单,只有一个停止事件传递给下一个监听器的方法,没有太多别的了。
通常,一个特定事件的数据都会保存到Event对象中,方便把数据传递给监听器。在kernel.response事件中,传入到监听器的Event对象是子类FliterResponseEvent的对象,FliterResponseEvent是Event的子类。FliterResponseEvent包含getReponse和setResponse方法,允许监听器获得或者修改Response对象。
总的来说:创建一个监听特定事件的监听器的时候,Event的一个子类会传入到监听器中,监听器可以通过该子类的方法获得和修改信息。
事件调度器(dispatcher)
dispatcher是事件调度系统的核心,一般来说,一个dispatcher内包含一个监听器的列表,当一个事件需要被调度的时候,dispatcher就会通知它包含的所有监听该事件的监听器。
1 use Symfony\Component\EventDispatcher\EventDispatcher;
2 
3 $dispatcher = new EventDispatcher();
关联监听器
把监听器添加到dispatcher上,监听特定的事件,那么该事件被调度的时候,dispatcher就会通知监听器工作了。dispatcher使用addListener方法把一个监听器(PHP callable)添加到一个事件上。
1 $listener = new AcmeListener();
2 $dispatcher->addListener('foo.action', array($listener, 'onFooAction'));
addListener方法有三个参数:
* 监听器需要监听是的事件的名称;
* 监听器(一个PHP callable);
* 一个可选的表示优先级的整数(数值越高优先级越高,监听器就会更早的被触发),默认为0,如果优先级一样,那么谁先添加就先触发;
PHP callable是指能作为参数传入call_user_func()或者传入is_callable()函数执行后返回true的PHP 变量。PHP callable可以是 \Closure实例,一个实现了__invoke方法的对象,或者是表示一个函数的字符串,或者一个表示对象方法或者类方法的数组。
到目前为止,我们看过把一个PHP对象作为监听器,我们也可以把Closure对象作为监听器。
1 use Symfony\Component\EventDispatcher\Event;
2 
3 $dispatcher->addListener('foo.action', function (Event $event) {
4     // will be executed when the foo.action event is dispatched
5 });
在上面的例子中,foo.action事件被调度,dispatcher就调用AcmeListener::onFooAction方法,并把Event对象作为唯一的参数传入方法中。
 1 use Symfony\Component\EventDispatcher\Event;
 2 
 3 class AcmeListener
 4 {
 5     // ...
 6 
 7     public function onFooAction(Event $event)
 8     {
 9         // ... do something
10     }
11 }

     在实际使用中,都是传入一个特定的Event子类的对象到监听器,例如FilterResponseEvent:

1 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
2 
3 public function onKernelResponse(FilterResponseEvent $event)
4 {
5     $response = $event->getResponse();
6     $request = $event->getRequest();
7 
8     // ...
9 }
创建和调度事件
除了系统内置的事件,我们也可以创建和调度自定义的事件。这是很有好处的,当我们使用第三方类库的时,还有可以使不同的组件之间解耦,使系统更灵活健壮。
静态的Events类
假如我们要创建一个事件——store.order——当订单被创建的时候就会被触发。
namespace Acme\StoreBundle;final class StoreEvents
{/*** The store.order event is thrown each time an order is created* in the system.** The event listener receives an* Acme\StoreBundle\Event\FilterOrderEvent instance.** @var string*/const STORE_ORDER = 'store.order';
}
这个类并没有什么方法,也不做什么操作,只是定义了事件名称,方便管理和组织事件。监听这个事件的监听器都会被传入一个FilterOrderEvent对象。
创建一个Event对象
接着,当你调度这个新的事件的时候,会创建一个Event对象传如到dispatcher的dispatch()方法,dispatcher就把这个Event对象传给所有的监听该事件的监听器。如果我们不需要向监听器传入任何信息,那么可以使用系统默认的Symfony\Component\EventDispatcher\Event 类。然而,很多时候,我们都需要传入特定的信息到监听器,那么我们可以创建一个类继承Symfony\Component\EventDispatcher\Event。
例如,我们需要在所有的监听器中传入order对象:
 1 namespace Acme\StoreBundle\Event;
 2 
 3 use Symfony\Component\EventDispatcher\Event;
 4 use Acme\StoreBundle\Order;
 5 
 6 class FilterOrderEvent extends Event
 7 {
 8     protected $order;
 9 
10     public function __construct(Order $order)
11     {
12         $this->order = $order;
13     }
14 
15     public function getOrder()
16     {
17         return $this->order;
18     }
19 }
所有监听器都可以通过FilterOrderEvent的getOrder方法获得order对象。
调度事件
dispatcher的dispatch()方法通知监听给定的事件的所有监听器,有两个参数,一个是需要调度的事件名,另一个就是传给所有监听器的Event对象。
 1 use Acme\StoreBundle\StoreEvents;
 2 use Acme\StoreBundle\Order;
 3 use Acme\StoreBundle\Event\FilterOrderEvent;
 4 
 5 // the order is somehow created or retrieved
 6 $order = new Order();
 7 // ...
 8 
 9 // create the FilterOrderEvent and dispatch it
10 $event = new FilterOrderEvent($order);
11 $dispatcher->dispatch(StoreEvents::STORE_ORDER, $event);

    FilterOrderEvent对象作为参数传入到dispatch方法,现在,任何监听store.order事件的监听器都会接收到FilterOrderEvent对象,并通过调用getOrder方法获得order对象。

1 // some listener class that's been registered for "store.order" event
2 use Acme\StoreBundle\Event\FilterOrderEvent;
3 
4 public function onStoreOrder(FilterOrderEvent $event)
5 {
6     $order = $event->getOrder();
7     // do something to or with the order
8 }
Event Subscribers  
最普遍的监听事件的方法是注册一个监听器到dispatcher中,一个监听器可以监听一个或者多个事件。
还有另一种监听事件的方法是使用Event SubScriber,Event SubScriber是一个PHP类,能够准确的告诉dispatcher它订阅了那些事件。实现EventSubscriberInterface接口,该接口有一个静态的方法getSubscriberdEvents。
namespace Acme\StoreBundle\Event;use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;class StoreSubscriber implements EventSubscriberInterface
{public static function getSubscribedEvents(){return array('kernel.response' => array(array('onKernelResponsePre', 10),array('onKernelResponseMid', 5),array('onKernelResponsePost', 0),),'store.order'     => array('onStoreOrder', 0),);}public function onKernelResponsePre(FilterResponseEvent $event){// ...
    }public function onKernelResponseMid(FilterResponseEvent $event){// ...
    }public function onKernelResponsePost(FilterResponseEvent $event){// ...
    }public function onStoreOrder(FilterOrderEvent $event){// ...
    }
}

这个监听器类很简单,告诉了dispatcher监听了什么事件,还有监听的事件触发的方法。addSubscriber()方法把subscriber注册到dispatcher。

1 use Acme\StoreBundle\Event\StoreSubscriber;
2 
3 $subscriber = new StoreSubscriber();
4 $dispatcher->addSubscriber($subscriber);
dispatcher准确的把Subscriber注册到EventSubscriberInterface::getSubscriberdEvents()返回的事件里,EventSubscriberInterface::getSubscriberdEvents()方法返回一个数组,数组的键对应Subscriber监听的事件,值对应这Subscriber处理该事件调用的一个方法或者一组方法。上面的例子中,一组监听器的方法对应这一个事件,同时我们也可以设置优先级来控制这组方法的执行先后顺序。当kernel.response事件被触onKernelResponsePreonKernelResponseMid, 和 onKernelResponsePost三个方法就会先后执行。
停止事件的传递
在一些情况下,监听器可以停止事件传递下去,防止后续的监听器被调用,换句话说,监听器必须通知dispatcher停止传递事件给后续的监听器。在监听器里面实现stopPropagation()方法:
1 use Acme\StoreBundle\Event\FilterOrderEvent;
2 
3 public function onStoreOrder(FilterOrderEvent $event)
4 {
5     // ...
6 
7     $event->stopPropagation();
8 }
那么,监听了store.order事件的还没有执行的监听器就不会在被执行。
通过isPropagationStopped()方法可以判断一个事件是否被停止。
1 $dispatcher->dispatch('foo.event', $event);
2 if ($event->isPropagationStopped()) {
3     // ...
4 }

 

转载于:https://www.cnblogs.com/szuyuan/p/4015843.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/375573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】漫谈ANN(2):BP神经网络

上一次我们讲了M-P模型,它实际上就是对单个神经元的一种建模,还不足以模拟人脑神经系统的功能。由这些人工神经元构建出来的网络,才能够具有学习、联想、记忆和模式识别的能力。BP网络就是一种简单的人工神经网络。我们的第二话就从BP神经网络…

给定一个值S,在有序数组中找出两个元素A和B,使 A+B = S.

在网上看到过一个面试题,感觉挺有意思,看别人的代码写的逻辑不够谨慎,重写了一个,较真了又。。。 package com.array7.algorithm;public class AlgorithmTest {public static void main(String[] args) {int[] arr {2 ,4 ,5 ,8 ,…

二叉树的最小深度

给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。 示例: 给定二叉树[3,9,20,null,null,15,7] 3/ \9 20/ \15 7返回它的最小深度 2. c 广度优先 /*** Definition for a b…

(转)会议期刊论文发表介绍(计算机科学领域)

转自:http://blog.csdn.net/babyfacer/archive/2009/07/25/4377552.aspx 一、计算机科学期刊介绍计算机科学的publication最大特点在于:极度重视会议,而期刊则通常只用来做re- publication。大部分期刊文章都是会议论文的扩展版,首…

笑男手札:SharePoint 2013 单一服务器场环境恢复数据库内容

SharePoint 2013 单一服务器场环境恢复数据库内容 笑男的公司服务很多客户,当然,这些客户都很挑剔,所以一般情况下生产(Prod)环境的服务是不能停的。 当然,如果你将包含相同网站集的数据库连接到同一个服务…

数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 示例 1: 输入:nums [4,1,4,6] 输出:[1,6] 或 [6,1]示例 2:…

【转】String Date Calendar之间的转换

1.Calendar 转化 String Calendar calendat Calendar.getInstance(); SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd"); String dateStr sdf.format(calendar.getTime()); 2.String 转化Calendar String str"2012-5-27"; SimpleDateFormat sd…

图解 深入浅出 JavaWeb:Servlet 再说几句

Writer :BYSocket(泥沙砖瓦浆木匠) 微 博:BYSocket 豆 瓣:BYSocket FaceBook:BYSocket Twitter :BYSocket 上一篇的《 Servlet必会必知 》受到大家一致好评 — (感谢 读…

react.js 从零开始(五)React 中事件的用法

事件系统 虚拟事件对象 事件处理器将会传入虚拟事件对象的实例,一个对浏览器本地事件的跨浏览器封装。它有和浏览器本地事件相同的属性和方法,包括 stopPropagation() 和 preventDefault(),但是没有浏览器兼容问题。 如果因为一些因素&#x…

乘积的最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。示例 2: 输入: [-2,0,-1] 输出…

javascript new

1. 仅function可以使用new 2. function使用new时,会拷贝function中this的内容给新对象,并将function的prototype指向新对象(如果该function没有prototype,则指向Object的prototype) 注:function本身不是Obj…

!+\v1 用来“判断浏览器类型”还是用来“IE判断版本”的问题!

这种写法是利用各浏览器对转义字符"\v"的理解不同来判断浏览器类型。在IE中,"\v"没有转义,得到的结果为"v"。而在其他浏览器中"\v"表示一个垂直制表符,所以ie解析的"\v1" 为 "v1&quo…

三个数的最大乘积

给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积。 示例 1: 输入: [1,2,3] 输出: 6示例 2: 输入: [1,2,3,4] 输出: 24注意: 给定的整型数组长度范围是[3,104],数组中所有的元素范围是[-1000, 1000]。 输入的数组中任…

VB.NET 数组的定义 动态使用 多维数组

我们都知道在全部程序设计语言中数组都是一个非常重要的概念,数组的作用是同意程序猿用同一个名称来引用多个变量,因此採用数组索引来区分这些变量。非常多情况下利用数组索引来设置一个循环,这样就能够高效地处理复杂的情况,因此…

web.xml 中的listener、 filter、servlet 加载顺序

1:首先是context-param节点 2:接着配置和调用listeners 并开始监听 3:然后配置和调用filters filters开始起作用 4:最后加载和初始化配置在load on startup的servlets转载于:https://www.cnblogs.com/dwchenxj/p/4787717.html

这么多个月,我头一次体验用类的概念来写驱动

原来感觉一样是那么爽阿。。。快乐得不得了。。。转载于:https://www.cnblogs.com/suanguade/p/4038190.html

设置Chrome忽略网站证书错误

本人在XP下使用Chrome。总是莫名其妙的提示整数错误,一部分https网站无法直接访问。网上找了下,把解决思路记录下来。 解决这个问题很简单,只需要修改你平时用来启动Chrome的快捷方式就可以忽略掉证书错误. 具体的操作方法是这样的: 找到你的Chrome快捷方…

Android开发之合并文件的几种方式

以下介绍合并文件的几种方式,并通过合并amr文件来举例介绍合并文件的详细流程。amr格式的文件头是6字节,所以在进行文件合并的时候要减去除第一个文件以外的其它文件的文件头。 注意:不同文件的文件头是不一样的,所以在合并的时候…

数组中出现次数超过一半的数

数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2限制&#xff1a; 1 < 数组长度 < 50000class Solution { pub…

中国寒龙反网络病毒联盟核心小组:官方公告,近期本站将会发布各种编程技术视频教程,详情请点击我们的以下公告!...

大家好&#xff0c;我是中国寒反网络病毒联盟官方客服&#xff01; 近期&#xff0c;本站将全面升级&#xff0c;本站发布各种编程视频教程&#xff0c;包括C,c#以及VB&#xff0c;VB.net&#xff0c;E&#xff0c;等相关编程语言入门视频教程&#xff0c;每天会定期更新视频教…