Hyperf 初体验-AOP 切面

什么是 AOP

再说 AOP 之前,肯定都知道 OOP 是什么,面向对象编程。那么 AOP 就是 面向切面编程

  • 通过预编译方式或运行期动态代理等技术实现程序功能的统一维护的一种技术。
  • OOP 是针对业务处理过程的实体及其属性和行为进行抽象和封装,以获得更加清晰高效的逻辑单元划分
  • AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或某个阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果

简单来说 切面就是 方法运行的前后执行的操作

如何定义切面

继承 AbstractAspect 类,每一个切面类差不多包含如下内容

<?php
namespace App\Aspect;

use App\Service\SomeClass;
use App\Annotation\SomeAnnotation;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;

/**
 * @Aspect
 */
class FooAspect extends AbstractAspect
{
    // 要切入的类,可以多个,亦可通过 :: 标识到具体的某个方法,通过 * 可以模糊匹配
    public $classes = [
        SomeClass::class,
        'App\Service\SomeClass::someMethod',
        'App\Service\SomeClass::*Method',
    ];

    // 要切入的注解,具体切入的还是使用了这些注解的类,仅可切入类注解和类方法注解
    public $annotations = [
        SomeAnnotation::class,
    ];

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 切面切入后,执行对应的方法会由此来负责
        // $proceedingJoinPoint 为连接点,通过该类的 process() 方法调用原方法并获得结果
        // 在调用前进行某些处理
        $result = $proceedingJoinPoint->process();
        // 在调用后进行某些处理
        return $result;
    }
}

切入类

假设我要把 IndexController@index 进行切面

<?php

namespace App\Aspect;

use App\Controller\IndexController;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;

/**
 * @Aspect()
 */
class IndexAspect extends AbstractAspect
{
    public $classes = [
        IndexController::class . '::' . 'index',
    ];

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $result = $proceedingJoinPoint->process();
        return '222'.$result . 'qqq';
    }
}

以上将会对 index 路由的返回值进行处理,进行改写。

切入注解
<?php

namespace App\Aspect;

use App\Annotation\User;
use App\Controller\IndexController;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;

/**
 * @Aspect()
 */
class IndexAspect extends AbstractAspect
{
    public $annotations = [
        User::class,
    ];

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $result = $proceedingJoinPoint->process();
        //获取注解参数
        /**
         * @var User $user
         */
        $user = $proceedingJoinPoint->getAnnotationMetadata()->class[User::class];
        return '222'.$result . $user->name;
    }
}

对应 控制器的内容

<?php

namespace App\Controller;

use App\Annotation\User;
use Hyperf\HttpServer\Annotation\AutoController;

/**
 * @AutoController()
 * @User("666666")
 */
class IndexController extends Controller
{

    public function index()
    {
        return 'Hello Hyperf!' ;
    }
}

注意,被切入的类 注解,只会在第一次起作用。比如修改 控制器返回值,再次请求会返回之前的记录。原因如下:

所有被 AOP 影响的类,都会在 ./runtime/container/proxy/ 文件夹内生成对应的 代理类缓存,服务启动时,如果类所对应的代理类缓存存在,则不会重新生成直接使用缓存,即使 Aspect 的切入范围发生了改变。不存在时,则会自动重新生成新的代理类缓存。

在部署生产环境时,我们可能会希望 Hyperf 提前将所有代理类提前生成,而不是使用时动态的生成,可以通过 php bin/hyperf.php di:init-proxy 命令来生成所有代理类,该命令会忽视现有的代理类缓存,全部重新生成。

基于以上,我们可以将生成代理类的命令和启动服务的命令结合起来,php bin/hyperf.php di:init-proxy && php bin/hyperf.php start 来达到自动重新生成所有代理类缓存然后启动服务的目的。

本地开发,我们可以直接将代理类缓存删掉,然后再重新启动服务即可.

暂无回复
0 / 180
网站公告

LaravelCode 正式上线啦

--->>>体验新站

今日诗词
正在加载今日诗词....
标签云
PHP Laravel API 随想 Node.js Linux Yii2.0 Redis 队列 JavaScript Vue Java MySQL 微信小程序 Python Hyperf 转载