天天看點

帶你學習hyperf-3.9 AOP 面向切面程式設計

3.9 AOP 面向切面程式設計

概念

AOP 為

Aspect Oriented Programming

的縮寫,意為:

面向切面程式設計

,通過動态代理等技術實作程式功能的統一維護的一種技術。AOP 是 OOP 的延續,也是 Hyperf 中的一個重要内容,是函數式程式設計的一種衍生範型。利用 AOP 可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

用通俗的話來講,就是在 Hyperf 裡可以通過

切面(Aspect)

介入到任意類的任意方法的執行流程中去,進而改變或加強原方法的功能,這就是 AOP。

注意這裡所指的任意類并不是完全意義上的所有類,在 Hyperf 啟動初期用于實作 AOP 功能的類自身不能被切入。

介紹

相對于其它架構實作的 AOP 功能的使用方式,我們進一步簡化了該功能的使用不做過細的劃分,僅存在

環繞(Around)

一種通用的形式:

  • 切面(Aspect)

    為對流程織入的定義類,包括要介入的目标,以及實作對原方法的修改加強處理
  • 代理類(ProxyClass)

    ,每個被介入的目标類最終都會生成一個代理類,來達到執行

    切面(Aspect)

    方法的目的

定義切面(Aspect)

每個 切面(Aspect) 必須實作 HyperfDiAopAroundInterface 接口,并提供 public 的 classes 和 annotations 屬性,為了友善使用,我們可以通過繼承 HyperfDiAopAbstractAspect 來簡化定義過程,我們通過代碼來描述一下。

<?php
namespace AppAspect;

use AppServiceSomeClass;
use AppAnnotationSomeAnnotation;
use HyperfDiAnnotationAspect;
use HyperfDiAopAbstractAspect;
use HyperfDiAopProceedingJoinPoint;

/**
 * @Aspect
 */
class FooAspect extends AbstractAspect
{
    // 要切入的類或 Trait,可以多個,亦可通過 :: 辨別到具體的某個方法,通過 * 可以模糊比對
    public $classes = [
        SomeClass::class,
        'AppServiceSomeClass::someMethod',
        'AppServiceSomeClass::*Method',
    ];

    // 要切入的注解,具體切入的還是使用了這些注解的類,僅可切入類注解和類方法注解
    public $annotations = [
        SomeAnnotation::class,
    ];

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 切面切入後,執行對應的方法會由此來負責
        // $proceedingJoinPoint 為連接配接點,通過該類的 process() 方法調用原方法并獲得結果
        // 在調用前進行某些處理
        $result = $proceedingJoinPoint->process();
        // 在調用後進行某些處理
        return $result;
    }
}           

複制

PHP

Copy

每個

切面(Aspect)

必須定義 code>@Aspect 注解或在 config/autoload/aspects.php 内配置均可發揮作用。

使用 @Aspect 注解時需 use HyperfDiAnnotationAspect; 命名空間;

您也可以通過 code>@Aspect 注解本身的屬性來完成切入目标的配置,通過下面注解的形式可以達到與上面的示例一樣的目的:

<?php
namespace AppAspect;

use AppServiceSomeClass;
use AppAnnotationSomeAnnotation;
use HyperfDiAnnotationAspect;
use HyperfDiAopAbstractAspect;
use HyperfDiAopProceedingJoinPoint;

/**
 * @Aspect(
 *   classes={
 *      SomeClass::class,
 *      "AppServiceSomeClass::someMethod",
 *      "AppServiceSomeClass::*Method"
 *   },
 *   annotations={
 *      SomeAnnotation::class
 *   }
 * )
 */
class FooAspect extends AbstractAspect
{
    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 切面切入後,執行對應的方法會由此來負責
        // $proceedingJoinPoint 為連接配接點,通過該類的 process() 方法調用原方法并獲得結果
        // 在調用前進行某些處理
        $result = $proceedingJoinPoint->process();
        // 在調用後進行某些處理
        return $result;
    }
}           

複制

PHP

Copy

代理類緩存

所有被 AOP 影響的類,都會在 ./runtime/container/proxy/ 檔案夾内生成對應的 代理類緩存,是否在啟動時自動生成取決于 config/config.php 配置檔案中 scan_cacheable 配置項的值,預設值為 false,如果該配置項為 true 則 Hyperf 不會掃描和生成代理類緩存,而是直接以現有的緩存檔案作為最終的代理類。如果該配置項為 false,則 Hyperf 會在每次啟動應用時掃描注解掃描域并自動的生成對應的代理類緩存,當代碼發生變化時,代理類緩存也會自動的重新生成。

通常在開發環境下,該值為 false,這樣更便于開發調試,而在部署生産環境時,我們可能會希望 Hyperf 提前将所有代理類提前生成,而不是使用時動态的生成,可以通過 php bin/hyperf.php 指令來生成所有代理類,然後再通過環境變量 SCAN_CACHEABLE 為 true 修改該配置項的值,以達到啟動時間更短、應用記憶體占用更低的目的。

基于以上,如果您使用 Docker 或 Kubernetes 等虛拟化技術來部署您的應用的話,您可以在鏡像建構階段就生成對應的代理類緩存并寫入到鏡像中去,在運作鏡像執行個體時,可大大減少啟動時間和應用記憶體。