[Spring.Net]Aop introduction–以performance log為例

[Spring.Net]Aop introduction–以performance log為例

前言
在開始介紹Aop之前,我想先介紹一下Proxy Pattern。而Aop的核心部分,就是在Dynamic Proxy的實作。接著我會用個簡單的例子,來說明如何透過Spring.Net來實作,以Aop的方式,來針對橫切面(Aspect)進行執行時間的紀錄。

Proxy Pattern
先來看一下Proxy Pattern的Class diagram:

proxy pattern class diagram

當Client呼叫ISubject的時候,以Proxy instance injection,而Proxy與RealSubject都有實作ISubject的介面,所以對Client來說,感受不到差異。(IoC)
而Proxy中,會使用到RealSubject,有點像轉播器,不管呼叫到什麼方法,核心都是執行RealSubject的方法,但在方法的前後,以及Proxy的初始化過程,則可以自行加入想要處理的事情。

例如:權限檢核、執行時間記錄、計費、使用次數紀錄、執行前後資料變化(軌跡稽核)等等。

Dynamic Proxy
使用Proxy固然很不錯,但是為了每一個interface,每一個concrete class去增加對應的Proxy class,那也太花功夫了。所以,Spring.Net透過Aop的方式替我們實作了Dynamic Proxy。讓我們可以更抽象的關注我們要處理的事情,把Subject被invoke的method抽象出來。動態的去產生proxy object,我們不用管proxy該怎麼設計,該實作什麼介面,invoke什麼方法,這些都是一樣的動作,所以透過Spring.Net framework,我們這個功夫就可以省下來。

Dynamic Proxy Pattern Class Diagram

亮黃色的部分,就是Spring.Net幫我們動態產生Proxy的部分。

而紫色的部分,則是4種Advice type,分別是:Before, After, Around跟Throw。Advice的作用,就是當使用Proxy Pattern時,我們加載『處理邏輯』上去的『時間點』。
Before是在呼叫原本Subject方法之前,
After是在呼叫原本方法之後,
Around則是方法前後,將原本被呼叫的Subject.method,抽象成invocation.Proceed(),
Throw則是針對方法拋出exception的例外處理。

如此一來,就可以讓Subject專心作Subject應該作的事,Aop的Advice module專心作Advice該作的事。對Client來說,被Interface隔著,根本就沒有感覺差異。透過Aop,將使用的class,被呼叫的class,以及額外加載的class耦合性降到最低。

實作說明
當沒有Aop的時候,我們呼叫Subject的method方式如下:

Call Method

當加上Aop之後(這邊的例子是記錄執行時間,所以以around advice為例),就類似替原本的硬碟,加上外接盒一樣,實際運作的還是原本的硬碟,但進出都會通過外接盒。(這也是Aspect橫切面的意義)

Aop implement Dynamic Proxy Pattern

我們接著來看一下實作的程式碼。

我們的需求很簡單,呼叫IJoeyService的GetSomething這個方法。然後去看這個方法實際執行花了多少毫秒。

Main:


	class Program
    {
        static void Main(string[] args)
        {
            IJoeyService joey = Core.WebUtility.Repository.JoeyService() as IJoeyService;
            string result = joey.GetSomething();
            Console.WriteLine(result);
            Console.ReadLine();
        }
    }

JoeyService:執行一個loop,從開始StartIndex到EndIndex結束,而這邊的例子,這兩個property是透過Dependency Injection來注入的。


	public  class JoeyService: IJoeyService
    {
       public string StartIndex { get; set; }
       public string EndIndex { get; set; }

        public string GetSomething()
        {
            int startIndex = Convert.ToInt32(this.StartIndex);
            int endIndex = Convert.ToInt32(this.EndIndex);

            StringBuilder result = new StringBuilder();
            for (int i = startIndex; i < endIndex; i++)
            {
                result.Append(i.ToString()+",");
            }
            return result.ToString();
        }
    }

接下來重頭戲了,Spring設定檔:Client會呼叫JoeyServiceProxy這個object,parent的部分,則是使用Spring.Net內建的Transaction管理機制(也是透過Aop去加載,在橫切面上進行transaction),而這個JoeyServiceProxy實際的Target為JoeyServiceWithAOP。

我們再看到JoeyServiceWithAOP這個object,type是Spring.Aop裡面的ProxyFactoryObject,也就是動態產生的Proxy物件,實際代理的Subject,則是Target裡面的JoeyService。而interceptorNames是Spring.Net內建的關鍵字,代表我們要加載上去的Advice有哪一些object,我們這邊加載的是performanceLoggingAroundAdvice這個object,而這個object會對應到我們的PerformanceLoggingAroundAdvice.cs。

config


PerformanceLoggingAroundAdvice.cs:可以看到我們實作了IMethodInterceptor,這個是around advice的interface,Invoke方法中的參數invocation,就是對應到實際的target,而Proceed()就是被呼叫的方法。我們在呼叫的前後,加上stopwatch來紀錄,實際呼叫這個target的method,究竟花了多少時間。

performance logging


執行時間結果:

execute time

結論
就這麼簡單,如果沒有使用Aop,那我們想要記錄每個subject的method實際執行時間,我們勢必不是修改到client的code,就是得修改Subject的code。而且這樣紀錄執行時間的程式,與Client和Subject的邏輯一點關係都沒有,放在Client或Subject的class裡面,就會違反單一職責原則,而且Advice modules的邏輯將散落一地,也不容易達到抽換或加入移除Advice的彈性。

Aop只是個聽起來很抽象、難懂的term,但實際的原理從proxy pattern的角度作切入,就會簡單的多。實作上搭配framework幫我們處理掉很多不必要的瑣碎事務,就能把更多的精神花在該關注的事務上。希望簡單的介紹,可以讓大家很容易就懂,Aop到底是什麼,為什麼需要Aop,以及怎麼透過Spring.Net來實作Aop。

Reference

  1. http://www.springframework.net/doc-1.1-P3/reference/html/aop.html
  2. http://www.codeproject.com/KB/architecture/AOP_UsingSpringPart1.aspx?display=Print

Sameple code download :  SpringWithAop.zip


blog 與課程更新內容,請前往新站位置:http://tdd.best/