[Spring.Net]Aop introduction–以performance log為例
前言
在開始介紹Aop之前,我想先介紹一下Proxy Pattern。而Aop的核心部分,就是在Dynamic Proxy的實作。接著我會用個簡單的例子,來說明如何透過Spring.Net來實作,以Aop的方式,來針對橫切面(Aspect)進行執行時間的紀錄。
Proxy Pattern
先來看一下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,我們這個功夫就可以省下來。
亮黃色的部分,就是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方式如下:
當加上Aop之後(這邊的例子是記錄執行時間,所以以around advice為例),就類似替原本的硬碟,加上外接盒一樣,實際運作的還是原本的硬碟,但進出都會通過外接盒。(這也是Aspect橫切面的意義)
我們的需求很簡單,呼叫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。
PerformanceLoggingAroundAdvice.cs:可以看到我們實作了IMethodInterceptor,這個是around advice的interface,Invoke方法中的參數invocation,就是對應到實際的target,而Proceed()就是被呼叫的方法。我們在呼叫的前後,加上stopwatch來紀錄,實際呼叫這個target的method,究竟花了多少時間。
執行時間結果:
結論
就這麼簡單,如果沒有使用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
- http://www.springframework.net/doc-1.1-P3/reference/html/aop.html
- http://www.codeproject.com/KB/architecture/AOP_UsingSpringPart1.aspx?display=Print
Sameple code download : SpringWithAop.zip
blog 與課程更新內容,請前往新站位置:http://tdd.best/