這篇文章給大家介紹的內容是YII2實現面向方面編程,有需要的朋友可以參考一下
引言:
軟體開發的目標是要對世界的部分元素或者資訊流建立模型,實現軟體系統的工程需要將系統分解成可以建立和管理的模組。於是出現了以系統模組化特性的物件導向程式設計技術。模組化的物件導向編程極度地提高了軟體系統的可讀性、複用性和可擴充性。向對象方法的焦點在於選擇對象作為模組的主要單元,並將對象與系統的所有行為聯絡起來。對象成為問題領域和計算過程的主要元素。但物件導向技術並沒有從本質上解決軟體系統的可複用性。建立軟體系統時,現實問題中存在著許多橫切關注點,比如安全性檢查、日誌記錄、效能監控,異常處理等,它們的實現代碼和其他商務邏輯代碼混雜在一起,並散落在軟體不同地方(直接把處理這些操作的代碼加入到每個模組中),這無疑破壞了OOP的"單一職責"原則,模組的可重用性會大大降低,這使得軟體系統的可維護性和複用性受到極大限制。這時候傳統的OOP設計往往採取的策略是加入相應的代理(Proxy)層來完成系統的功能要求,但這樣的處理明顯使系統整體增加了一個層次的劃分,複雜性也隨之增加,從而給人過於厚重的感覺。由此產生了面向方面編程(AOP)技術。這種編程模式抽取出散落在軟體系統各處的橫切關注點代碼,並模組化,歸整到一起,這樣進一步提高軟體的可維護性、複用性和可擴充性。
AOP簡介:
AOP: Aspect Oriented Programming 面向切面編程。
面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP),是目前軟體開發中的一個熱點。利用AOP可以對商務邏輯的各個部分進行隔離,從而使得商務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
AOP是OOP的延續,是(Aspect Oriented Programming)的縮寫,意思是面向切面(方面)編程。
主要的功能是:日誌記錄,效能統計,安全控制,交易處理,異常處理等等。
主要的意圖是:將日誌記錄,效能統計,安全控制,交易處理,異常處理等代碼從商務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導商務邏輯的方法中,進而改變這些行為的時候不影響商務邏輯的代碼。
可以通過先行編譯方式和運行期動態代理實現在不修改原始碼的情況下給程式動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。
假設把應用程式想成一個立體結構的話,OOP的利刃是縱向切入系統,把系統劃分為很多個模組(如:使用者模組,文章模組等等),而AOP的利刃是橫向切入系統,提取各個模組可能都要重複操作的部分(如:許可權檢查,日誌記錄等等)。由此可見,AOP是OOP的一個有效補充。
注意:AOP不是一種技術,實際上是編程思想。凡是符合AOP思想的技術,都可以看成是AOP的實現。
AOP 的基本概念:
在物件導向編程中,類,對象,封裝,繼承,多態等概念是描述物件導向思想主要術語。與此類似,在面向方面編程中,同樣存在著一些基本概念:
連接點(JointPoint) :一個連接程式執行過程中的一個特定點。典型的連接點有:調用一個方法;方法執行這個過程本身;類初始化;對象初始化等。連接點是 AOP 的核心概念之一,它用來定義在程式的哪裡通過 AOP 加入新的邏輯。
切入點(Pointcut) :一個切入點是用來定義某一個通知該何時執行的一組連接點。通過定義切入點,我們可以精確地控製程序中什麼組件接到什麼通知。上面我們提到,一個典型的連接點是方法調用,而一個典型的切入點就是對某一個類的所在方法調用的集合。通常我們會通過組建複雜的切入點來控制通知什麼時候被執行。
通知(Advice) :在某一個特定的連接點處啟動並執行代碼稱為“通知”。通知有很多種,比如
在連接點之前執行的前置通知(before advice)和在連接點之後執行的後置通知(after advice) 。
方面(Aspect) :通知和切入點的組合叫做方面,所以,方面定義了一段程式中應該包括的邏輯,以及何時應該執行該邏輯。
織入(Weaving) :織入是將方面真正加入程式碼的過程。對於靜態 AOP 方案而言,織入是在編譯時間完成的,通常是在編譯過程中增加一個步驟。類似的,動態 AOP 方案則是在程式運行是動態織入的。
目標(Target) :如果一個對象的執行過程受到某一個 AOP 的修改,那麼它就叫一個目標對象。目標對象通常也稱為被通知對象。
引入(Introduction) : 通過引入,可以在一個對象中加入新的方法或屬性,以改變它的結構,這樣即使該對象的類沒有實現某一個介面,也可以修改它,使之成為該介面的一個實現。
靜態和動態:靜態 AOP 和動態 AOP 兩者之間的區別主要在於什麼時間織入,以及如何織入。最早的 AOP 實現大多都是靜態。在靜態 AOP 中,織入是編譯過程的一個步驟。用Java 的術語說,靜態 AOP 通過直接對位元組碼進行操作,包括修改代碼和擴充類,來完成織入過程。顯然,這種辦法產生的程式效能很好,因為最後的結果就是普通的 Java 位元組碼,在運行時不再需要特別的技巧來確定什麼時候應該執行通知。這種方法的缺點是,如果想對方面做什麼修改,即使只是加入一個新的連接點,都必須重新編譯整個程式。AspectJ 是靜態 AOP 的一個典型例子。與靜態 AOP 不同,動態 AOP 中織入是在運行時動態完成的。織入具體是如何完成的,各個實現有所不同。Spring AOP 採取的方法是建立代理,然後代理在適當的時候執行通知。動態 AOP 的一個弱點就在於,其效能一般不如靜態 AOP。而動態AOP 的主要優點在於可以隨時修改程式的所有方面,而不需重新編譯目標。
AOP實踐:
YII2架構本身擁有一個功能,叫做行為.它可以動態為當前的類附加額外的功能,但這種功能在代碼層級結構是靜態,有侵入性的。
下面以YII2框架組成go!aop庫為例,介紹在YII2中如何?AOP編程.(go!aop簡介,可以參考go!aop的官網.)
由於YII架構擁有自己的類載入器,所在整合go!aop的時候,不能正常的工作,所以要將其禁用掉,使用composer提供的類載入器。
如下代碼所示(這裡使用YII2進階應用程式模板):
1、找到 spl_autoload_register(['Yii', 'autoload'], true, true); (PROJECT_PATH/vendor/yiisoft/yii2/Yii.php) 將其禁用掉.
2、執行 composer require goaop/framework
3、修改composer.json檔案,加入如下程式碼片段:
"autoload": { "psr-4": { "backend\\": "backend//", "frontend\\": "frontend//", "common\\": "common//" } }
4、 在frontend 目錄下建立一個components是目錄,並建立一個類AopAspectKernel,例如:
namespace frontend\components;use frontend\aspects\MonitorAspect;use Go\Core\AspectContainer;use Go\Core\AspectKernel;class AopAspectKernel extends AspectKernel{ protected function configureAop(AspectContainer $container) { $container->registerAspect(new MonitorAspect()); }}
5、在forntend目錄下在建立一個類InitAopComponent,並使其實現BootstrapInterface,使其可以在YII2架構引導時被自動引導
namespace frontend\components;use yii\base\BootstrapInterface;class InitAopComponent implements BootstrapInterface{ public function bootstrap($app) { print_r(\Yii::$app->params['aop']); $applicationAspectKernel = AopAspectKernel::getInstance(); $applicationAspectKernel->init(\Yii::$app->params['aop']); }}
6、在frontend/config/params.php中新增如下代碼:
'aop' => [ 'debug' => true, 'appDir' => dirname(__DIR__), 'cacheDir' => dirname(__DIR__) . '/runtime/aop', 'includePaths' => [ dirname(__DIR__) ] ]
7、在frontend下面建立aspects目錄,並建立類MonitorAspect,代碼如下:
namespace frontend\aspects;use Go\Aop\Aspect;use Go\Aop\Intercept\MethodInvocation;use Go\Lang\Annotation\Before;class MonitorAspect implements Aspect{ /** * Method that will be called before real method * * @param MethodInvocation $invocation Invocation * @Before("execution(public frontend\components\AopTestComponent->*(*))") */ public function beforeMethodExecution(MethodInvocation $invocation) { $obj = $invocation->getThis(); echo 'Calling Before Interceptor for method: ', is_object($obj) ? get_class($obj) : $obj, $invocation->getMethod()->isStatic() ? '::' : '->', $invocation->getMethod()->getName(), '()', ' with arguments: ', json_encode($invocation->getArguments()), "<br>\n"; }}
9、修改frontend/config/main.php檔案,並在components數組下新增一個key,代碼如下:
'components'=>[ 'aop' => [ 'class' => 'frontend\components\InitAopComponent' ] ]
10、修改frontend/config/main.php檔案,並在bootstrap數組下新增aop值,代碼如下:
'bootstrap'=>['log','aop']
至此,YII2整合go!aop完成...