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 相关文章推荐
一个程序下载的管理程序(四)
Oct 09 PHP
PHP游戏编程25个脚本代码
Feb 08 PHP
基于php 随机数的深入理解
Jun 05 PHP
PHP中使用循环实现的金字塔图形
Nov 08 PHP
PHP IDE PHPStorm配置支持友好Laravel代码提示方法
May 12 PHP
php实现简单的上传进度条
Nov 17 PHP
eclipse php wamp配置教程
Jun 30 PHP
php获取字符串前几位的实例(substr返回字符串的子串用法)
Mar 08 PHP
PHP写的简单数字验证码实例
May 23 PHP
PHP面向对象五大原则之里氏替换原则(LSP)详解
Apr 08 PHP
laravel 自定义常量的两种方案
Oct 14 PHP
PhpStorm连接服务器并实现自动上传功能
Dec 09 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生成excel时单元格内换行问题的解决方法
2010/08/26 PHP
PHP使用CURL实现对带有验证码的网站进行模拟登录的方法
2014/07/23 PHP
PHP实现克鲁斯卡尔算法实例解析
2014/08/22 PHP
yii的CURD操作实例详解
2014/12/04 PHP
php实现的简易扫雷游戏实例
2015/07/09 PHP
Javascript异步编程的4种方法让你写出更出色的程序
2013/01/17 Javascript
JavaScript实现网页图片等比例缩放实现代码及调用方式
2013/02/25 Javascript
jQuery中removeData()方法用法实例
2014/12/27 Javascript
js超时调用setTimeout和间歇调用setInterval实例分析
2015/01/28 Javascript
jquery实现最简单的滑动菜单效果代码
2015/09/12 Javascript
ECHO.js 纯javascript轻量级延迟加载的实例代码
2016/05/24 Javascript
js获取所有checkbox的值的简单实例
2016/05/30 Javascript
jQuery 翻页组件yunm.pager.js实现div局部刷新的思路
2016/08/11 Javascript
php输出全部gb2312编码内的汉字方法
2017/03/04 Javascript
关于jQuery.ajax()的jsonp碰上post详解
2017/07/02 jQuery
Angular6 写一个简单的Select组件示例
2018/08/20 Javascript
详解js中Array的方法及技巧
2018/09/12 Javascript
js防抖和节流的深入讲解
2018/12/06 Javascript
js设计模式之单例模式原理与用法详解
2019/08/15 Javascript
微信小程序图片自适应实现解析
2020/01/21 Javascript
python用10行代码实现对黄色图片的检测功能
2015/08/10 Python
实例讲解Python编程中@property装饰器的用法
2016/06/20 Python
对python 数据处理中的LabelEncoder 和 OneHotEncoder详解
2018/07/11 Python
Django ORM 自定义 char 类型字段解析
2019/08/09 Python
基于torch.where和布尔索引的速度比较
2020/01/02 Python
Python读取二进制文件代码方法解析
2020/06/22 Python
浅析图片上传及canvas压缩的流程
2020/06/10 HTML / CSS
哈利波特商店:Harry Potter Shop
2018/11/30 全球购物
ALDO美国官网:加拿大女鞋品牌
2018/12/28 全球购物
九年级历史教学反思
2014/01/27 职场文书
节约用电标语
2014/06/17 职场文书
2014年国庆节广播稿
2014/09/19 职场文书
研究生简历自我评
2015/03/11 职场文书
物业工程部主管岗位职责
2015/04/16 职场文书
感谢师恩主题班会
2015/08/17 职场文书
Java移除无效括号的方法实现
2021/08/07 Java/Android