若转载教程,请注明出自SW-X框架官方文档
AOP
,即面向切面编程,可以说是OOP
,面向对象编程的补充和完善。
面向切面编程是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景:
日志
事物
数据库操作
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中。
所谓交叉业务逻辑是指,通用的,与主业务逻辑无关的代码,如安全检查,事物,日志等。若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。
这样,会使业务逻辑变得混杂不清。
举个例子:
银行系统取款会有一个流程查询也会有一个流程。
这两者,都有一个相同的验证用户的流程
这个时候 AOP
就可以来帮我们简化代码了,首先,写代码的时候可以不写这个验证用户的步骤,即完全不考虑验证用户,写完之后,在另外一个地方,写好验证用户的代码,然后告诉 PHP 你要把这一段代码加到哪几个地方,PHP就会帮你加过去,这里还只是两个地方,如果有多个控制流,这样写代码会大大节约时间。
而且 AOP
不会把代码加到源文件里,但是它会正确的影响最终的机器代码。
上面那个 验证用户 的方框,我们可以把它当成一块板子,在这块板子上插入一些控制流程,这块板子就可以当成是 AOP
中的一个切面。
所以 AOP
的本质是在一系列的纵向的控制流程中,把那些相同的子流程提取成一个横向的面,把纵向流程画成一条直线,而 AOP
相当于把相同的地方连起来了
再来一幅图理解一下:
这个验证用户的子流程 就成了一条直线,也可以理解成一个切面,这里只插了三个流程,如果其他流程也需要这个子流程,也可以插到其他地方去。
AOP术语
术语 | 说明 |
---|---|
切面 | 切面泛指交叉业务逻辑。比如事物处理,日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强 |
织入 | 织入是指将切面代码插入到目标对象的过程 |
连接点 | 连接点指切面可以织入的位置 |
切入点 | 切入点指切面具体织入的位置 |
通知 | 通知(Advice)是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 |
顾问 | 顾问(Advisor)是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。不仅指定了切入时间点,还可以指定具体的切入点。 |
AOP的实现方式
通知类型 | 说明 |
---|---|
前置通知(MethodBeforeAdvice) | 目标方法执行之前调用 |
后置通知(AfterReturningAdvice) | 目标方法执行完成之后调用 |
环绕通知(MethodInterceptor) | 目标方法执行前后都会调用方法,且能增强结果 |
异常处理通知(ThrowsAdvice) | 目标方法出现异常调用 |
深入理解
在传统的编写业务逻辑处理代码时,我们通常会习惯性地做几件事情:
日志记录
事务控制
权限控制
然后才是编写核心的业务逻辑处理代码。
当代码编写完成回头再看时,不禁发现,扬扬洒洒上百行代码中,真正用于核心业务逻辑处理才那么几行,如下图所示。
方法复方法,类复类,就这样带着无可奈何遗憾地度过了多少个春秋。
这倒也罢,倘若到了项目的尾声,突然决定在权限控制上需要进行大的变动时,成千上万个方法又得一一”登门拜访”,痛苦”雪上加霜”。
如果能把上图中众多方法中的所有共有代码全部抽取出来,放置到某个地方集中管理,然后在具体运行时,再由容器动态织入这些共有代码的话,最起码可以解决两个问题:
Java EE
框架(PHP的大部分框架也支持)的程序员在编写具体的业务逻辑处理方法时,只需关心核心的业务逻辑处理,既提高了工作效率,又使代码变更简洁优雅。
在日后的维护中由于业务逻辑代码与共有代码分开存放,而且共有代码是集中存放的,因此使维护工作变得简单轻松。
面向切面编程AOP
技术就是为解决这个问题而诞生的,AOP
切面其实就是横切面,如下图所示,代表的是一个普遍存在的共有功能,例如,日志切面、权限切面及事务切面等。
下面我们以用户管理业务逻辑组件UserService
的AOP
实现过程(见下图)为例,深度剖析一下AOP技术的实现原理。
AOP技术是建立在反射机制与动态代理机制之上的。
业务逻辑组件在运行过程中,AOP容器会动态创建一个代理对象供使用者调用,该代理对象已经按Java EE程序员的意图将切面成功切入到目标方法的连接点上,从而使切面的功能与业务逻辑的功能同时得以执行。
从原理上讲,调用者直接调用的其实是AOP容器动态生成的代理对象,再由代理对象调用目标对象完成原始的业务逻辑处理,而代理对象则已经将切面与业务逻辑方法进行了合成。
PHP实现案例
没使用AOP之前的代码:
<?php
App::run();
// 框架执行类理解为 APP:run
class App {
public static function run() {
Bank::run();
}
}
// 业务核心类,理解为controller吧
class Bank {
public static function run() {
# 验证用户
if ($_GET['name'] != '小黄牛') {
die('验证码用户错误了');
}
# 取款
if ($_GET['type'] == 1) {
echo '用户取款拉';
error_log('取出300', 3, 'error.log');
} else if ($_GET['type'] == 2) {
echo '用户在查询余额';
error_log('查询显示500', 3, 'error.log');
}
echo '退出柜员机~~';
error_log('正常退出', 3, 'error.log');
}
}
用AOP优化之后:
<?php
App::run();
// 框架执行类理解为 APP:run
class App {
public static function run() {
# 假设这是我们当前访问的Controller
$controller = 'Bank';
# 下面我们就不做AOP是否有定义的操作了,还有AOP内是否有规范创建4大实现了
# 如果要做得到话,自己用PHP的反射机制实现
$class = $controller.'Aop';
$AOP = new $class();
# 执行前置
$AOP->BeforeAdvice();
# 执行环绕
$AOP->InterceptorAdvice();
# 异常处理
try{
# 执行应用
Bank::run();
} catch(Exception $e) {
$AOP->ThrowsAdvice($e);
}
# 执行环绕
$AOP->InterceptorAdvice();
# 执行后置
$AOP->ReturningAdvice();
}
}
// AOP类,定义了4大实现
class BankAop {
// 前置
public function BeforeAdvice() {
# 验证用户
if ($_GET['name'] != '小黄牛') {
die('验证码用户错误了');
}
}
// 后置
public function ReturningAdvice() {
WebLog::run('正常退出');
}
// 环绕
public function InterceptorAdvice() {
}
// 异常处理
public function ThrowsAdvice($Exception) {
# 我这里抓住,然后打印
echo 'Message: ' .$Exception->getMessage();
}
}
// 日志写入,自己的定义呗
class WebLog {
public static function run($error) {
return error_log($error, 3, 'error.log');
}
}
// 业务核心类,理解为controller吧
class Bank {
public static function run() {
# 取款
if ($_GET['type'] == 1) {
echo '用户取款拉';
WebLog::run('取出300');
} else if ($_GET['type'] == 2) {
echo '用户在查询余额';
WebLog::run('查询显示500');
}
echo '退出柜员机~~';
}
}
这样一来,核心业务的代码就少很多了,上面的例子是将AOP绑定在了controller上。真实开发中我们可能一个AOP公用到多个controller上,也可能独立被某个模块单独触发引用,所以上述案例只是通过controller讲解了AOP的应用,并不是说明只能这样用,不用被固有思维影响了自主逻辑。
免费教程手写不易,希望能支持一下SW-X框架,(^.^)
GitHub有账号的朋友,也可以给我们一个小星星噢!
该群以Swoole生态发展交流为主,若出现争吵,攻击其他人等行为,立即剔除。