AOP 使用入门及适用场景
AOP 与Middleware 非常相似, 当你熟练掌握Middleware 时, 相当于也掌握了AOP
我们先回顾下Middleware 调用流程:
可以通过配置或者注解使用Middleware
1 2 3 4 5 6 7 8 9
| Router::get('/', 'App\Controller\IndexController::index', ['middleware' => [CorsMiddleware::class]]);
#[Middleware(CorsMiddleware::class)] class IndexController { ... }
|
Middleware 作为解耦神器, 非常普遍的应用在各个服务中
但是, 这么好用的功能仅能作用在Controller 上, 岂不是很可惜?
AOP 可以简单理解为, 可以作用在任何Class 的Middleware:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| use Hyperf\Di\Aop\ProceedingJoinPoint;
class A { public function test() { echo 'this A'.PHP_EOL; } }
class B { public function test() { (new A())->test(); echo 'this B'.PHP_EOL; } }
(new B())->test();
class AOP { public array $classes = [ A::class, B::class, ]; public function process(ProceedingJoinPoint $proceedingJoinPoint) { $className = $proceedingJoinPoint->className; $methodName = $proceedingJoinPoint->methodName;
echo "{$className}::{$methodName} Before".PHP_EOL;
$result = $proceedingJoinPoint->process();
echo "{$className}::{$methodName} After".PHP_EOL; return $result; } }
|
AOP 使用场景有哪些? 可以思考Middleware 使用场景有哪些:
- Token 鉴权
- 签名/验签, 加密/解密
- 日志/Tracer
- CORS
- ….
以上场景抽象一下, 得到结论: Middleware 适用于系统功能
同理, AOP 也适用于以上场景, 以及各种系统功能
- Cacheable
- AsyncQueueMessage
- Retry
- Breaker
- Transaction
- RateLimit
- …
以常用的事务举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| use Hyperf\DbConnection\Db;
class A { public function foo() { Db::beginTransaction(); try{ $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); Db::commit(); } catch(\Throwable $e){ Db::rollBack(); } return $model; }
public function bar() { return Db::transaction(function () { $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); return $model; }); } }
|
AOP 搭配注解写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| use Hyperf\DbConnection\Annotation\Transactional;
class A { #[Transactional] public function test() { $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); return $model; } }
class TransactionAspect { public array $annotations = [ Transactional::class, ];
public function process(ProceedingJoinPoint $proceedingJoinPoint) { return Db::transaction(function () use ($proceedingJoinPoint) { return $proceedingJoinPoint->process(); } ); } }
|
单一场景下, 闭包写法与注解AOP 差别不大
但如果此时需要加需求, 易读性会截然不同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A { public function bar() { return Cache::remember(function () { Db::transaction(function () { $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); return $model; }); }); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| class A { #[Cacheable] #[Transactional] public function test() { $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); return $model; } }
|
AOP 避免回调地狱, 举例, 非正常业务场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A { #[Transactional] #[Cacheable] #[AtomicLock] #[Retry] #[RateLimit(create: 1, capacity: 3)] public function test() { $model = Model::query()->where('id', 1)->first(); $model->name = 'Hyperf'; $model->save(); return $model; } }
|