一.简单概述:
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;
}