php笔记之:AOP的应用


Posted in PHP onApril 24, 2013

介绍
 
你以前听说过AOP(Aspect Oriented Programming)吗?虽然在php方面,好像没有过多的使用,但是在企业级开发中,AOP被广泛使用。我将借此文,向大家介绍PHP方面的AOP。

这篇文章主要解释AOP的概念。

 
什么是AOP?
 
在应用开发中,我们经常发现需要很多功能,这些功能需要经常被分散在代码中的多个点上,但是这些点事实上跟实际业务没有任何关联。比如,在执行一些特殊任务之前需要确保用户是在登陆状态中,我们把这些特殊人物就叫做"cross-cutting concerns",让我们通过Wikipedia来了解一下"cross-cutting concerns"(横向关系)的定义。
在计算机科学中,"cross-cutting concerns"指的是“切面(或方向)编程”。这些关系不能从其他系统(框架设计或者某些实现)中很好的分解出来,以至于出现代码重复,在系统中存在有意义的依赖关系,或者两者兼有之。
 现在你对于“横向关系”应该有一个基础的认识,让我们看看他们在代码中是怎么样的?

假设一种场景,你是一个博客站点的编辑。你需要登陆站点,然后进行创建帖子,验证帖子,编辑帖子等等。如果你没有登陆,那么你应该直接到登陆界面。为了确保这些行为是安全的,以上的任何操作都需要进行有效验证,代码如下。

<?php
class BlogPost extends CI_Controller
{
    public function createPost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
            Messages::notifyAdmin();
        }
    }
    public function approvePost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
        }
    }
    public function editPost() {
        if (!Authentication::checkAuthentication()) {
            // redirect to login
        }
        else {
            // proceed
        }
    }
    public function viewPost() {
        // ...
    }
}

 看上面的代码,你会发现在每个方法之前都调用了checkAuthentication(),因为这些行为需要用户登陆之后才能进行。还有就是notifyAdmin()来辨别是否是管理员帐号,以便创建新贴。看见没有,有很多“重复的代码”,而且BlogPost类,应该仅负责管理帖子。验证和辨别身份应当是分离的。我们违反了“单一职责原则”。

单一职责原则讲述的是每个类应该只有单一的责任(任务),而且应该把整个责任都封装在一个类中。所有服务应该按照职责严谨而均衡的进行分布。

 迄今为止,我们能够明白AOP所表达的意思。横向切面关系被成组的放进一个类中,我们管这个类叫“切面”。从我们核心代码中分离横向切面关系的过程就叫做Aspect Oriented Programming。

AOP专业术语

有很多条件专门用于解释AOP的特性。理解这些条件将是你成功把AOP集成到你的项目中的钥匙。
Aspect
Advice
Joinpoint
Pointcut
我们已经学习到切面(Aspect)是什么!现在让我们了解一下其他三个条件意味着什么?

Advice(通知)
Advice用于调用Aspect(切面),正如其名所暗示,Advice用于定义某种情况下做什么和什么时间做这件事情。在我们之前的例子中,checkAuthentication(做什么)是advice(通知),在指定方法中它应该在执行代码之前(什么时间)被调用。

 
Joinpoint(接入点)
Joinpoint是我们创建Advice应用中的位置。再翻看之前的代码,你会发现我调用了几个与业务逻辑没有直接关联的功能。在createPost()中,如,cross-cutting concerns应该在执行验证逻辑之前和发送信息给管理员之后发生。这些都可能是接入点。

在你的应用代码中,接入点可以放置在任何位置。但是Advice仅能在某些点中布置,这要根据你的AOP框架,过后我会讨论。

Pointcut(点切割)
 点切割定义了一种把通知匹配到某些接入点的方式。虽然在我们的例子中只有一对接入点,但是在你的应用中你可以放置上千个接入点,你也不需要把通知应用到所有的接入点上。你可以把一些你认为有必要的接入点绑定到通知上。

假设我们想要通知 createPost(),approvePost() 和 editPost(),但是现在没有viewPost()。我们使用某种方法把这三种方法绑定到通知上。之后我们创建一个包含切面细节的XML文件,这些细节包含一些匹配接入点的正则表达式。

总结:当有横向切入关系存在于我们的应用的时候,我们可以创建一个切面,这个切面在一些选择使用点切割的接入点上应用通知功能。

 
AOP 通知类型
 

通知代码我们可以用很多中方式表现。我之前提到,这些通知代码依赖你使用的框架,但是有些你需要熟悉的类型,请看下面:

前通知

返回后通知

抛出后通知

周边通知

前通知
在你的代码中一些特殊点之前使用通知——正常是调用一个方法。

迄今为止,为了简化概念和为了让你更快的理解你的代码,我经常把通知写到方法里。但是在真实的环境里,通知经常是不写在方法里的。应该有一个独立的控制器,每个方法都在这个控制器里,而且每个方法都包裹着AOP的功能。这个全局的控制器运行在整个系统里,而且对我们是不可见的。

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        Authentication::checkAuthentication();
        $classObj = new $className();
        $classObj->$funcName();
    }
}

在这里假设有这么一个类,主要是用于给你展现这个类实际上发生了什么事情。假设那个controlPaths方法是应用中全局切入点,访问应用中的每个方法都需要通过这个方法访问。上面的方法中在执行每个方法之前,我们调用了通知checkAuthentication()。——这就是前通知。

返回后通知

这个通知在指定功能执行完后只执行一次,并且返回那个访问点。考虑下面的代码:

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        $classObj = new $className();
        $classObj->$funcName();
        Database::closeConnection();
    }
}

按 Ctrl+C 复制代码注意这里,当方法完成之后,我们清理了数据库资源。在返回通知之后,我们调用这个通知。

抛出后通知
如果在执行进程期间函数抛出异常,那么在抛出完异常之后应用通知。这里是抛出完异常之后,通知就变成错误提示。

<?php
class PathController
{
    function controlPaths($className, $funcName) {
        try {
            $classObj = new $className();
            $classObj->$funcName();
        }
        catch (Exception $e) {
            Error::reportError();
        }
    }
}

 

周边通知
第四种通知是周边通知,他是前通知和返回后通知的合并体。

 <?php
class PathController
{
    function controlPaths($className, $funcName) {
        Logger::startLog();
        $classObj = new $className();
        $classObj->$funcName();
        Logger::endLog();
    }
}
PHP 相关文章推荐
剖析 PHP 中的输出缓冲
Dec 21 PHP
PHP中函数内引用全局变量的方法
Oct 20 PHP
在命令行下运行PHP脚本[带参数]的方法
Jan 22 PHP
DEDE采集大师官方留后门的删除办法
Jan 08 PHP
php删除文件夹及其文件夹下所有文件的函数代码
Jan 23 PHP
PHP生成图片验证码、点击切换实例
Jun 25 PHP
ThinkPHP模板判断输出Empty标签用法详解
Jun 30 PHP
php下Memcached入门实例解析
Jan 05 PHP
在Linux系统的服务器上隐藏PHP版本号的方法
Jun 06 PHP
详解PHP的Laravel框架中Eloquent对象关系映射使用
Feb 26 PHP
PHP implode()函数用法讲解
Mar 08 PHP
php png失真的原因及解决办法
Oct 24 PHP
php class中self,parent,this的区别以及实例介绍
Apr 24 #PHP
PHP中::、-&amp;gt;、self、$this几种操作符的区别介绍
Apr 24 #PHP
php判断终端是手机还是电脑访问网站的思路及代码
Apr 24 #PHP
用PHP提取中英文词语以及数字的首字母的方法介绍
Apr 23 #PHP
基于PHP读取TXT文件向数据库导入海量数据的方法
Apr 23 #PHP
『PHP』PHP截断函数mb_substr()使用介绍
Apr 22 #PHP
基于magic_quotes_gpc与magic_quotes_runtime的区别与使用介绍
Apr 22 #PHP
You might like
PHP实现简单登录界面
2019/10/23 PHP
javascript 在网页中的运用(asp.net)
2009/11/23 Javascript
JS input文本框禁用右键和复制粘贴功能的代码
2010/04/15 Javascript
js 中 document.createEvent的用法
2010/08/29 Javascript
js内置对象 学习笔记
2011/08/01 Javascript
设置iframe的document.designMode后仅Firefox中其body.innerHTML为br
2012/02/27 Javascript
javascript实现图片切换的幻灯片效果源代码
2012/12/12 Javascript
Extjs4中tree的拖拽功能(可以两棵树之间拖拽) 简单实例
2013/12/08 Javascript
原生JS实现响应式瀑布流布局
2015/04/02 Javascript
JavaScript中的toUTCString()方法使用详解
2015/06/12 Javascript
Jquery遍历select option和添加移除option的实现方法
2016/08/26 Javascript
详解vue之页面缓存问题(基于2.0)
2017/01/10 Javascript
JS中showModalDialog关闭子窗口刷新主窗口用法详解
2017/03/25 Javascript
vue数字类型过滤器的示例代码
2017/09/07 Javascript
从零开始学习搭建React脚手架项目
2018/08/23 Javascript
python进阶_浅谈面向对象进阶
2017/08/17 Python
python 全局变量的import机制介绍
2017/09/07 Python
Python字符串的全排列算法实例详解
2019/01/07 Python
python 函数的缺省参数使用注意事项分析
2019/09/17 Python
matplotlib绘制鼠标的十字光标的实现(内置方式)
2021/01/06 Python
英国最大的宝石首饰超市:QP Jewellers
2018/09/23 全球购物
英国设计的甲板鞋和船鞋:Chatham
2018/12/06 全球购物
与世界上最好的跑步专业品牌合作:Fleet Feet
2019/03/22 全球购物
绢花、人造花和人造花卉:BLOOM
2019/08/07 全球购物
swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上
2013/07/06 面试题
毕业生毕业总结的自我评价范文
2013/11/02 职场文书
教师队伍管理制度
2014/01/14 职场文书
全国道德模范事迹
2014/02/01 职场文书
技能竞赛活动方案
2014/02/21 职场文书
投标授权委托书范文
2014/08/02 职场文书
师德师风自我评价范文
2014/09/11 职场文书
就业推荐表院系意见
2015/06/05 职场文书
大学毕业谢师宴致辞
2015/07/27 职场文书
2016关于读书活动的心得体会
2016/01/14 职场文书
2019年大学毕业生个人自我鉴定范文大全
2019/03/21 职场文书
python中的被动信息搜集
2021/04/29 Python