這篇文章跟大家介紹的內容是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完成...
相关推荐:
以上是YII2實現面向方面編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!