swoft2.0的AOP源码实现

一.简单概述:
AOP的目标类一旦被切面类定义切割时,swoft会动态生成其代理类接管目标所有的功能。
二.源码实现:
2.1 文件路径
Swoft/vendor/swoft/bean/src/Container.php
2.2 在newBean方法检测是否有类有代理

private function newBean(string $beanName, string $id = '')
    {
		//低相关代码...
		// 如果有代理,则把代理类注入容器
        if ($this->handler) {
            $oldCName  = $className;
            $className = $this->handler->classProxy($className);

            // New class name from handler->classProxy()
            if ($oldCName !== $className) {
                $reflectClass = new ReflectionClass($className);
            }
        }
        $reflectObject   = $this->newInstance($reflectClass, $constructArgs);
        //低相关代码...
        return $this->setNewBean($beanName, $scope, $reflectObject, $id);
    }

2.3 classProxy方法在文件Swoft/vendor/swoft/framework/src/BeanHandler.php中,最终是依赖Swoft/vendor/swoft/proxy/src/Proxy.php的newClassName中完成。
2.4 newClassName方法中的关键代码,返回动态代理类名。

public static function newClassName(string $className, Visitor $visitor, string $suffix = ''): string
    {
        //...
        //动态代理文件路径,代理类名$proxyName等于目标类名+PROXY+unique()
        $proxyFile = sprintf('%s/%s.php', Sys::getTempDir(), $proxyName);
        //动态生成的代理类的代码
        $proxyCode = sprintf('<?php %s %s', PHP_EOL, $proxyCode);
        //生成代理类文件
        $result = file_put_contents($proxyFile, $proxyCode);
        // 加载代理类文件
        self::loadProxyClass($proxyFile);
        return $newClassName;
    }

2.5 动态生成的代理类的例子说明。

/*
\App\Http\Controller\TestController是目标类,TestController_PROXY_614837c0ac6a4是动态生成的类
其中的hi方法是目标方法。
可以看到是通过继承的方式代理了实际的目标类。
*/
class TestController_PROXY_614837c0ac6a4 extends \App\Http\Controller\TestController
{
    use \Swoft\Aop\Concern\AopTrait;
    public function getOriginalClassName() : string
    {
        return 'App\\Http\\Controller\\TestController';
    }
    public function hi() : Response
    {
        return $this->__proxyCall('App\\Http\\Controller\\TestController', 'hi', func_get_args());
    }

2.6 \Swoft\Aop\Concern\AopTrait的__proxyCall方法

public function __proxyCall(string $className, string $methodName, array $args)
    {
        $mathAspects = Aop::match($className, $methodName);
        if (!$mathAspects) {
            return $this->__invokeTarget($methodName, $args);
        }

        /* @var AspectHandler $aspectHandler */
        $aspectHandler = bean(AspectHandler::class);
        $aspectHandler->setTarget($this);
        $aspectHandler->setMethodName($methodName);
        $aspectHandler->setArgs($args);
        $aspectHandler->setAspects($mathAspects);
        $aspectHandler->setClassName($className);

        $argsMap = $this->getArgsMap($className, $methodName, $args);
        $aspectHandler->setArgsMap($argsMap);

        return $aspectHandler->invokeAspect();
    }

2.7 最终在Swoft/vendor/swoft/aop/src/AspectHandler.php的invokeAspect方法里执行通知和目标方法。

public function invokeAspect()
    {
        $around = $this->aspect['around'] ?? [];
        $after  = $this->aspect['after'] ?? [];
        $afRetn = $this->aspect['afterReturning'] ?? [];
        $afThw  = $this->aspect['afterThrowing'] ?? [];
        $result = null;
        try {
            if (!empty($around)) {
                // 调用环绕通知
                $result = $this->invokeAdvice($around);
            } else {
                // 调用目标方法和前置通知
                $result = $this->invokeTarget();
            }
        } catch (Throwable $e) {
            $this->throwable = $e;
        }
        // 调用后置通知
        if (!empty($after)) {
            $this->invokeAdvice($after);
        }
        // 调用异常通知
        if (!empty($this->throwable)) {
            if (!empty($afThw)) {
                // Invoke afterThrowing advice
                return $this->invokeAdvice($afThw, $this->throwable);
            }
            throw $this->throwable;
        }
        // 调用返回通知
        if (!empty($afRetn)) {
            $result = $this->invokeAdvice($afRetn, null, $result);
        }
        return $result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值