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实现验证码功能
Oct 09 PHP
输出控制类
Oct 09 PHP
在PHP中读取和写入WORD文档的代码
Apr 09 PHP
php计算十二星座的函数代码
Aug 21 PHP
深入理解curl类,可用于模拟get,post和curl下载
Jun 08 PHP
基于php中使用excel的简单介绍
Aug 02 PHP
php通过数组实现多条件查询实现方法(字符串分割)
May 06 PHP
大家在抢红包,程序员在研究红包算法
Aug 31 PHP
阿里云PHP SMS短信服务验证码发送方法
Jul 11 PHP
PHP迭代器和迭代的实现与使用方法分析
Apr 19 PHP
YII框架实现自定义第三方扩展操作示例
Apr 26 PHP
PHP中类与对象功能、用法实例解读
Mar 27 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
MySQL时间字段究竟使用INT还是DateTime的说明
2012/02/27 PHP
PHP+jquery+ajax实现即时聊天功能实例
2014/12/23 PHP
php内嵌函数用法实例
2015/03/20 PHP
DEDECMS首页调用图片集里的多张图片
2015/06/05 PHP
php的闭包(Closure)匿名函数初探
2016/02/14 PHP
HTML-CSS群中单选引发的“事件”
2007/03/05 Javascript
用js实现的仿sohu博客更换页面风格(简单版)
2007/03/22 Javascript
js类中获取外部函数名的方法与代码
2007/09/12 Javascript
firefox火狐浏览器与与ie兼容的2个问题总结
2010/07/20 Javascript
教您去掉ie网页加载进度条的方法
2010/12/09 Javascript
无缝滚动js代码通俗易懂(自写)
2013/06/19 Javascript
JavaScript的常见兼容问题及相关解决方法(chrome/IE/firefox)
2013/12/31 Javascript
JavaScript页面模板库handlebars的简单用法
2015/03/02 Javascript
js实现将选中内容分享到新浪或腾讯微博
2015/12/16 Javascript
JS基于正则截取替换特定字符之间字符串操作示例
2017/02/03 Javascript
详解如何用模块化的方式写vuejs
2017/12/16 Javascript
Vue框架之goods组件开发详解
2018/01/25 Javascript
JS实现DOM删除节点操作示例
2018/04/04 Javascript
浅谈在不使用ssr的情况下解决Vue单页面SEO问题(2)
2018/11/08 Javascript
搭建基于express框架运行环境的方法步骤
2018/11/15 Javascript
[53:49]LGD vs Fnatic 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python实现查询苹果手机维修进度
2015/03/16 Python
详解Python中find()方法的使用
2015/05/18 Python
Python函数装饰器原理与用法详解
2019/08/16 Python
Python发送邮件的实例代码讲解
2019/10/16 Python
QML用PathView实现轮播图
2020/06/03 Python
全面总结使用CSS实现水平垂直居中效果的方法
2016/03/10 HTML / CSS
HTML5 语音搜索只需一句代码
2013/01/03 HTML / CSS
美国在线纱线商店:Darn Good Yarn
2019/03/20 全球购物
积极分子思想汇报
2014/01/04 职场文书
公司管理建议书范文
2014/03/12 职场文书
五五普法心得体会
2014/09/04 职场文书
测量JavaScript函数的性能各种方式对比
2021/04/27 Javascript
idea编译器vue缩进报错问题场景分析
2021/07/04 Vue.js
深入解析MySQL索引数据结构
2021/10/16 MySQL
基于PyQT5制作一个桌面摸鱼工具
2022/02/15 Python