Symfoy2 HttpKernel事件驱动

HttpKernel:事件驱动

Symfony2 框架层和应用层的工作都是在 HttpKernel::handle()方法中完成,HttpKernel::handle() 的内部的实现其实是通过调度事件(HttpKernel内的事件监听器)来完成的,相当于把所有组件都整合成完整的应用。

使用 HttpKernel 很简单,只需要创建一个 EventDispatcher(事件分发器) 和 controller resolver(Controller解析器),可以实现更多的事件监听器丰富应用的功能:

1) kernel.request Event

实现kernel.request事件目的是为了添加更多信息到Request对象,或者得到返回的Response对象(例如:从缓存中获取又或者security层拒绝访问)

kernel.request事件是HttpKernel::handle()调度的第一个事件,那么监听该事件的多个监听器就会被执行。

监听kernel.request事件的监听器多种多样,它们的行为各不相同,例如security监听器判断用户没有足够的权限的时候一个RedirectResponse对象,如果当前直接返回Response对象,那么就会直接执行 kernel.response 事件:

其它的监听器实现一些初始化或者添加更多的信息到Request对象。

例如路由监听器,路由监听器从Request对象中获取路由相关的信息,经过进一步加工,确定处理请求的Controller,并且把相应的信息保存到Request对象里的‘attribute’里,这些信息都会被Controller解析器使用的,所以可以实现监听器之间的解耦。

总得来说,实现kernel.request事件的目的要么就是直接创建和返回Response对象,要么就是添加更多的信息到Request对象。

RouterListener是Symfony框架中实现kernel.request事件的最重要监听器,RouterListener在路由层中执行,返回一个包含符合当前请求的路由信息的数组,例如路由匹配模式里面的_controller和请求的参数({name})。这些信息都会存放在Request里的attributes数组里面,目前只是会添加路由信息到Request对象中还没有做其它的动作,但是解析Controller的时候会被用到。

2) Resolve the Controller

假设实现kernel.request事件的时候没有创建和返回Response对象,那么下一步就是确定、解析controller和controller需要的参数。controller部分是应用层的最后一个堡垒,负责创建和返回包含一个特定页面的Response对象。如何确定被请求的controller完全取决于应用程序,这个工作有controller解析器来完成——一个是实现ControllerResolverInterface的类,同时也是HttpKernel构造函数的一个参数。

实现ControllerResolverInterface的两个方法getController和getArguments:

HttpKernel::handle()首先调用controller resolver 的 getController()方法,并向该方法传入Request对象,controller resolver根据Request包含的信息确定并返回controller。

第二个方法,getArguments()会在kernel.controller事件被调度的时候执行。

解析Controller

Symfony框架使用内置的ControllerResolver(实质上是使用了一个有额外的功能的子类),该解析器利用了RouterListener保存到Request对象的attributes属性里信息来确定controller。

 

getController

ControllerResolver 在Request对象的attributes数组中查找 _controller 键(这些信息实际上是由RouterListener存放进Request对象中的):

 

a) 如果_controller 键对应AcmeDemoBundle:Default:index 这个格式的值,那么该值就包含了类名和方法名,可以被Symfony框架解析成为,例如:Acme\DemoBundle\Controller\DefaultController::indexAction,这个转换是由Symfony框架的特定的ControllerResolver的子类完成的。

 

b) 你的controller类会被实例化,而且该controller类必须包含一个无参的构造函数。

 

c) 如果你的controller还实现了ContainerAwareInterface,那么setContainer方法就会被调用,container就会被注入到controller中,这个实现也是由Symfony框架的特定的ControllerResolver的子类完成的。

 

上面也有一些其他变化过程,例如你把你的controller配置成为service。

 

      

        3) The kernel.controller Event

kernel.controller事件是在controller被执行前初始化一些信息或者改变controller对象。

被调用的controller确定之后,HttpKernel::handle()就会调度kernel.controller事件。在系统的某部分被确定后(例如:controller、路由信息等)但是这些部分被执行前,监听kernel.controller事件的监听器就会运行了。

kernel.controller

Symfony框架有几个kernel.controller事件的监听器,多数都是处理分析数据的。

 

SensioFrameworkExtraBundle中一个监听器ParamConverter允许我们把一个对象作为参数传入到controller中,而不是字符串或者数值参数。

 

4)获得controller的参数

getAttributes()方法是返回一个参数数组,这个参数数组会被传递给controller,我们也可以自定义该方法,也可以使用Symfony框架内置的。

到了这一步,kernel已经获得了controller和controller运行时需要的参数了。

 

Symfony框架获得controller的参数

ControllerResolver使用放射机制获得被调用的controller的方法的参数列表。遍历该列表,使用下面的步骤来确定参数列表中一一对应的值:

 

a) 使用参数作为键查找Request对象中的attributes数组,如果找到,那么相应的值会传入到controller的方法中,例如:controller方法的第一个参数是$name,那么在Request的attributes数组中包含$attributes[‘name‘]的值,那么$attributes[‘name‘]就会被使用。

 

b) 如果该该参数在Symfony配置routing的时候被指定,那么就会跳过对该参数的查找。

 

        5)调用controller

这一步,controller就会被执行。

controller会创建包含特定页面或者json的Response对象,这也是应用层的最后一个步骤。

如果controller返回的是Response对象,那么下一步kernel.response事件就会触发,否者kernel.view事件就会被触发。

controller必须要有返回值,如果返回null,程序会报错


        6) kernel.view事件

controller返回值不是Response对象的时候被触发。

如果controller返回的不是Response对象,kernel.view事件会被触发,kernel.view事件的目的是把controller返回的值创建一个Response对象。

监听实现kernel.view事件也是很有好处的,如果controller返回了html页面或者json字符串,我们可以通过监听kernel.view事件,把html页面或者json字符串封装到Response对象,那么Symfony框架依旧可以正常运行。

但是如果没有监听器没有设置Response对象到事件中,那么程序就会报错。either the controller or one of the view listeners must always return a Response.

 

Symfony框架中实现kernel.view事件

 

Symfony框架中没有缺省的监听器实现kernel.view事件,可是,有一个核心Bundle——SensioFrameworkExtraBundle里有个监听改事件的监听器。如果你的controller返回一个数组,并且在controller类的顶部有@Template的注解,那么该监听器就会渲染一个模板,把controller返回的数组传入到模板中,最后利用模板返回的内容创建一个Response对象,并返回该Response对象。

 

除此之外,FOSRestBundle也实现了监听该事件的监听器,a listener on this event which aims to give you a robust view layer capable of using a single controller to return many different content-type responses (e.g. HTML, JSON, XML, etc).

 

 

        7) kernel.response 事件

      在发送Response对象到客户端前修改它。

kernel的目的是把Request对象转换成为Response对象。Response对象可能是在kernel.request事件中创建,可能是由controller返回,又或者是由监听kernel.view事件的监听器返回。

不管是在哪一个环节创建Response对象,最后kernel.response事件都会被触发。监听kernel.response事件的监听器都会以某种方式修改Response对象,例如:修改Response的header部分,修改cookie,或者甚至会修改Response对象返回的内容(注入javascript到</body>标签前等等)

kernel.response事件完成后,HttpKernel::handle()返回最终的Response对象,调用Response::send()箱客户端发送headers头部和Response实体。

Symfony框架实现kernel.response事件

 

Symfony框架内置几个监听器监听kernel.response事件,更多的可以通过开发者社区获得。例如:在dev开发环境下WebDebugToolbarListener向页面的底部注入javascript代码,debug工具条就会显示出来。还有另一个监听器,ContextListener序列化当前用户的信息保存到session中,下一次请求的时候直接在session中重载用户信息。

 

 

        8) kernel.terminate事件

监听该事件的监听器通常都是处理一些耗时的后台程序。

HttpKernel进程的最后一个事件是kernel.terminate事件,而且该事件的触发是在HttpKernel::handle()方法之后,并且响应的内容已经发送给用户。

正如上面的截图所示,在向客户端发送响应内容后才调用terminate方法,terminate方法内触发kernel.terminate事件,监听该事件的监听器就会运行,这些监听器都是在发送响应信息到客户端后执行的。(例如发送邮件)

Symfony里监听kernel.terminate事件的监听器

 

 Symfony框架一个完整的工作流程

使用HttpKernel组件的时候,我们不需要实现任何监听器添加到内核事件中,也不需要实现controller resolver。HTTp组件自带的监听器和controller resolver就能够正常工作了:

 子请求

除了把“main request”传入到HttpKernel::handle之外,还可以把所谓的“sub request”传入HttpKernel::handle中。子请求看起来和其它的请求差不多,不同的是,一般的请求是渲染完整的一个页面,而子请求渲染的是一个页面的一部分。通常我们都是在controller里面创建一个子请求(或者在模板里面创建)。

HttpKernel::handle方法运行子请求的时候,需要修改第二个参数的值:

子请求也是创建一个完整的请求——响应周期。唯一不同的是,有些监听器可能只会在“main request”中运行(security)。KernelEvent的子类传递给监听器,监听器通过KernelEvent::getRequestType()判断当前请求是“main request”还是“sub request”。

例如一个监听器只会在“main request”的请求下才会执行:

时间: 2024-11-08 18:27:54

Symfoy2 HttpKernel事件驱动的相关文章

Symfoy2源码分析——启动过程2

上一篇分析Symfony2框架源码,探究Symfony2如何完成一个请求的前半部分,前半部分可以理解为Symfony2框架为处理请求做准备工作,包括container生成.缓存.bundls初始化等一些列准备工作(Symfoy2源码分析——启动过程1).而这一篇讲的是Symfony2如何根据请求的数据生成Response对象,向客户端返回响应数据. 在分析前需要了解Symfony2的事件驱动机制:Symfony2事件驱动. 言归正传,Symfony2请求的工作流程其实是Symfony2内核的事件

Symfoy2目录结构说明

了解框架的目录结构是框架快速入门的一个途径,一个成熟的框架,每个功能模块都被划分存放在不同的目录. Symfony2一级目录结构: ├── app //这目录下包含了,配置文件(应用的配置文件会被import到这里面的配置文件中才生效).缓存的类.缓存的模板 ├── bin ├── composer.json ├── composer.lock ├── LICENSE ├── README.md ├── src //我们编写的应用存放在这个目录下(包含Controller.Model.View.

11.python并发入门(part13 了解事件驱动模型))

一.事件驱动模型的引入. 在引入事件驱动模型之前,首先来回顾一下传统的流水线式编程. 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束 每一个代码块里是完成各种各样事情的代码,但编程者知道代码块A,B,C,D...的执行顺序,唯一能够改变这个流程的是数据.输入不同的数据,根据条件语句判断,流程或许就改为A--->C--->E...--->结束.每一次程序运行顺序或许都不同,但它的控制流程是由输入数据和

Linux的I/O模式、事件驱动编程模型

大纲: (1)基础概念回顾 (2)Linux的I/O模式 (3)事件驱动编程模型 (4)select/poll/epoll的区别和Python示例 网络编程里常听到阻塞IO.非阻塞IO.同步IO.异步IO等概念,总听别人装13不如自己下来钻研一下.不过,搞清楚这些概念之前,还得先回顾一些基础的概念. 1.基础知识回顾 注意:咱们下面说的都是Linux环境下,跟Windows不一样哈~~~ 1.1 用户空间和内核空间 现在操作系统都采用虚拟寻址,处理器先产生一个虚拟地址,通过地址翻译成物理地址(内

单片机的非OS的事件驱动

单片机的非OS的事件驱动 Part 1 前言 很多单片机项目恐怕都是没有操作系统的前后台结构,就是main函数里用while无限循环各种任务,中断处理紧急任务.这种结构最简单,上手很容易,可是当项目比较大时,这种结构就不那么适合了,编写代码前你必须非常小心的设计各个模块和全局变量,否则最终会使整个代码结构杂乱无序,不利于维护,而且往往会因为修改了某部分代码而莫名其妙的影响到其他功能,而使调试陷入困境. 改变其中局面的最有效措施当然是引入嵌入式操作系统,但是大多数的操作系统都是付费的(特别是商业项

实验二:事件驱动-回调函数实现爬虫

承接上一节课,我们在上节课中提到,socket I/O 阻塞时的那段时间完全被浪费了,那么要如何节省下那一段时间呢? 非阻塞I/O 如果使用非阻塞I/O,它就不会傻傻地等在那里(比如等连接.等读取),而是会返回一个错误信息,虽然说是说错误信息,它其实就是叫你过一会再来的意思,编程的时候都不把它当错误看. 非阻塞I/O代码如下: sock = socket.socket() sock.setblocking(False) try: sock.connect(('xkcd.com', 80)) ex

SOA EDA 事件驱动架构 (Event-Driven Architecture,EDA) 简介

事件驱动架构 (Event-Driven Architecture,EDA) 简介 可以从两个方面来理解 EDA: EDA 是一种侧重于以生成/消费为基础的异步通信的架构模式.这主要对照于传统的基于线程的同步系统. EDA 是一种以事件 (event)为核心,提供事件产生,路由,消费已经结果回调等机制的架构模式. 简单地说, 面向服务架构 (Service-Oriented Architecture, SOA) 是一种 IT 架构策略,其基于面向服务的概念之上.自从 2002 开始为大家熟知以来

图形用户界面编程——事件驱动编程

根据事件发生而执行代码的编程方式,称为事件驱动编程 事件源——>监听——>处理事件(处理函数) ps:实现按键全选和反选 运行结果图

DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能

DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能 一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户下完订单之后,店家或管理员可以对客户订单进行跟踪和操作.上一专题我们已经实现创建订单的功能,则接下来自然就是后台管理功能的实现了.所以在这一专题中将详细介绍如何在网上书店案例中实现后台管理功能. 二.后台管理中的权限管理的实现 后台管理中,