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
用来给图片加水印的PHP类
Apr 09 PHP
php 函数使用方法与函数定义方法
May 09 PHP
简单的PHP留言本实例代码
May 09 PHP
Ubuntu 16.04下安装PHP 7过程详解
Mar 28 PHP
PHP编程实现csv文件导入mysql数据库的方法
Apr 29 PHP
PHP检查网站是否宕机的方法示例
Jul 24 PHP
PHP利用Mysql锁解决高并发的方法
Sep 04 PHP
php实现微信企业转账功能
Oct 02 PHP
PHP标准库(PHP SPL)详解
Mar 16 PHP
如何让PHP编码更加好看利于阅读
May 12 PHP
thinkphp框架使用JWTtoken的方法详解
Oct 10 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
盘点被央视点名过的日本动画电影 一部比一部强
2020/03/08 日漫
php安全开发 添加随机字符串验证,防止伪造跨站请求
2013/02/14 PHP
php+html5基于websocket实现聊天室的方法
2015/07/17 PHP
简单了解PHP编程中数组的指针的使用
2015/11/30 PHP
微信公众号OAuth2.0网页授权问题浅析
2017/01/21 PHP
PHP文件操作实例总结【文件上传、下载、分页】
2018/12/08 PHP
Laravel 集成微信用户登录和绑定的实现
2019/12/27 PHP
ImageFlow可鼠标控制图片滚动
2008/01/30 Javascript
风吟的小型JavaScirpt库 (FY.JS).
2010/03/09 Javascript
jQuery 获取和设置select下拉框的值实现代码
2013/11/08 Javascript
js使用Array.prototype.sort()对数组对象排序的方法
2015/01/28 Javascript
jquery+html5时钟特效代码分享(可设置闹钟并且语音提醒)
2020/03/30 Javascript
jQuery fancybox在ie浏览器下无法显示关闭按钮的解决办法
2016/02/19 Javascript
原生js制作日历控件实例分享
2016/04/06 Javascript
微信小程序  modal详解及实例代码
2016/11/09 Javascript
vue2利用Bus.js如何实现非父子组件通信详解
2017/08/25 Javascript
Vue 实现展开折叠效果的示例代码
2018/08/27 Javascript
用vuex写了一个购物车H5页面的示例代码
2018/12/04 Javascript
vue 内置过滤器的使用总结(附加自定义过滤器)
2018/12/11 Javascript
利用d3.js实现蜂巢图表带动画效果
2019/09/03 Javascript
[54:24]Optic vs TNC 2018国际邀请赛小组赛BO2 第二场
2018/08/18 DOTA
如何处理Python3.4 使用pymssql 乱码问题
2016/01/08 Python
Python基础篇之初识Python必看攻略
2016/06/23 Python
浅析Windows 嵌入python解释器的过程
2019/07/26 Python
Python编程快速上手——选择性拷贝操作案例分析
2020/02/28 Python
keras实现图像预处理并生成一个generator的案例
2020/06/17 Python
python 爬虫网页登陆的简单实现
2020/11/30 Python
HTML5 Canvas实现平移/放缩/旋转deom示例(附截图)
2013/07/04 HTML / CSS
英国家庭家具、照明和花园家具购物网站:Furniture123
2018/12/31 全球购物
业务部门经理岗位职责
2014/02/23 职场文书
我与祖国共奋进演讲稿
2014/09/13 职场文书
小学教师党员承诺书
2015/04/27 职场文书
英语投诉信范文
2015/07/03 职场文书
Python Socket编程详解
2021/04/25 Python
总结Python变量的相关知识
2021/06/28 Python
SQL SERVER存储过程用法详解
2022/02/24 SQL Server