Spring.Net AOP的通知類型及通知鏈
首先介紹AOP通知的概念。
通知(Advice):AOP框架在某個連接點(方法)中所采取的行為。在Spring.Net的通知類型分為環繞通知、前置通知、后置通知、異常通知。這四中通知類型以及這幾通知綜合運用形成的通知鏈。
關于各種通知類型我實現的編程方式、配置方式兩種給大家介紹。這一節主要說說上述通知類型中的后三種通知,前一種通知在上一節中已經做了說明,所以不作為本節的重點了,但是幾種通知的應用大致是一樣的。
本節重點分如下兩部分:
一、AOP的四種通知類型。
二、通知鏈的應用
首先還是介紹一下開發環境以及軟件版本:
VS版本:VS2008 SP1、Spring版本:1.3.0。
一、AOP四種通知類型介紹
1、前置通知。我理解的前置通知是在目標對象連接點執行前的通知。
編程方式實現。這種方式主要應用了程序集Spring.Net中Spring.Aop.Framework命名空間下的ProxyFactory(代理工廠)類和Spring.Aop命名控件下的IMethodBeforeAdvice接口。
目標對象的代碼如下:
代碼
1 public interface ICommand
2 {
3 void Execute();
4 }
5
6 class ServiceCommand:ICommand
7 {
8 public void Execute()
9 {
10 Console.WriteLine("ServiceCommand execute");
11 }
12 }
通知的實現代碼:
代碼
1 class ConsoleLoggingBeforeAdvice:IMethodBeforeAdvice
2 {
3 public void Before(MethodInfo method, object[] args, object target)
4 {
5 Console.WriteLine("Method name is :{0}", method.Name);
6 if (args != null)
7 {
8 if (args.Length > 0)
9 {
10 for (int i = 0; i < args.Length; i++)
11 {
12 Console.WriteLine(" the {0} position argments in args is {1}", i, args[i]);
13 }
14 }
15 }
16 else
17 {
18 Console.WriteLine("args is null");
19 }
20 Console.WriteLine("the type target is {0}",target.GetType().ToString());
21 }
22 }
實現代碼如下:
代碼
1 ProxyFactory factory = new ProxyFactory(new ServiceCommand());
2 factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
3 ICommand command = (ICommand)factory.GetProxy();
4 command.Execute();
運行結果圖如下:

配置方式實現。配置代碼如下:
代碼
1 <objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop">
2 <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
3 <property name="Target">
4 <object type="SpringBeforeAdvice.ServiceCommand" singleton="0"></object>
5 </property>
6 <property name="InterceptorNames">
7 <list>
8 <value>beforeAdvice</value>
9 </list>
10 </property>
11 </object>
12 <object id="beforeAdvice" type="SpringBeforeAdvice.ConsoleLoggingBeforeAdvice"></object>
13 </objects>
實現代碼如下:
代碼
1 IApplicationContext context = ContextRegistry.GetContext();
2 ICommand command2 = (ICommand)context["myServiceObject"];
3 command2.Execute();
4 IDictionary dic = context.GetObjectsOfType(typeof(ICommand));
5 foreach (DictionaryEntry item in dic)
6 {
7 Console.WriteLine("key is :{0},value is :{1}", item.Key, item.Value);
8 }
運行結果如下圖:

可以看到:出了運行連接點Execute執行運行的代碼以外,還有通知采取的行為(Before的執行)。如果通過斷點調試,可以看到在連接點執行前,Before執行了。
2、后置通知。后置通知正好是前置通知相反,是在目標對象連接點執行后的通知。它是實現了Spring.Aop命名空間下的IAfterReturningAdvice接口。
編程方式實現。
目標對象的實現代碼和前置通知實現方式相同,這里就不給出實現代碼了。后置通知的實現代碼是:
代碼
1 class ConsoleLoggingAfterAdvice: IAfterReturningAdvice
2 {
3 public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target)
4 {
5 if (returnValue != null)
6 {
7 Console.WriteLine("the type of returnValue is :{0},returnValue is {1}", returnValue.GetType().ToString(), returnValue);
8 }
9 Console.WriteLine("method name is {0}", method.Name);
10 if (args != null && args.Length > 0)
11 {
12 foreach (object obj in args)
13 {
14 Console.WriteLine(obj);
15 }
16 }
17 Console.WriteLine("the type of target is :{0},returnValue is {1}", target.GetType().ToString(), target);
18 }
19 }
使用代碼:
代碼
1 ICommand target = new ServiceCommand();
2 ProxyFactory factory = new ProxyFactory(target);
3 factory.AddAdvice(new ConsoleLoggingAfterAdvice());
4
5 ICommand command = (ICommand)factory.GetProxy();
6 command.Execute("test string");
運行結果如下圖:

配置方式實現。配置文件如下:
代碼
1 <objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop">
2
3 <object id ="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject" >
4 <property name="Target">
5 <object type="SpringAfterAdvice.ServiceCommand"></object>
6 </property>
7 <property name="InterceptorNames">
8 <list>
9 <value>afterAdvice</value>
10 </list>
11 </property>
12 </object>
13
14 <object id="afterAdvice" type="SpringAfterAdvice.ConsoleLoggingAfterAdvice" ></object>
15 </objects>
實現代碼:
1 IApplicationContext context = ContextRegistry.GetContext();
2 ICommand command2 = (ICommand)context["myServiceObject"];
3 command2.Execute("config string");
運行結果如下圖:

與前置通知相反,后置通知在目標對象連接點執行后再執行。
3、異常通知。我理解的是當目標對象的連接點執行出現異常的時候執行。異常通知是實現了Spring.Aop命名空間下的IThrowingAdvice接口。
編程方式實現:
異常通知實現代碼如下:
代碼
1 public void AfterThrowing(FormatException ex)
2 {
3 Console.WriteLine("異常通知執行");
4 Console.WriteLine(ex.Message);
5 }
6
7
8 public void AfterThrowing(UnauthorizedAccessException ex)
9 {
10 Console.Out.WriteLine("Advised method threw this exception : " + ex);
11 }
使用代碼如下:
代碼
1 ICommand target = new ServiceCommand();
2 ProxyFactory factory = new ProxyFactory(target);
3 factory.AddAdvice(new AroudAdvice());
4 factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
5
6 ICommand command = (ICommand)factory.GetProxy();
7 command.Execute();

配置方式實現。配置文件如下:
代碼
1 <objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop">
2
3 <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
4 <property name="Target">
5 <object type="SpringExceptionAdvice.ServiceCommand"></object>
6 </property>
7 <property name="InterceptorNames">
8 <list>
9 <value>aroundAdvice</value>
10 <value>exceptionAdvice</value>
11 </list>
12 </property>
13 </object>
14
15 <object id="exceptionAdvice" type="SpringExceptionAdvice.ConsoleLoggingThrowsAdvice"></object>
16 <object id="aroundAdvice" type="SpringExceptionAdvice.AroudAdvice"></object>
17 </objects>
1 IApplicationContext context = ContextRegistry.GetContext();
2 ICommand command2 = (ICommand)context["myServiceObject"];
3 command2.Execute();

注意:異常通知實現的IThrowsAdvice中沒有任何方法。但是它的實現類中必須實現public void AfterThrowing方法。并且只有目標對象的連接點執行過程中發生異常類型與AfterThrowing方法的參數一樣時,異常通知才會發生。當然,AfterThrowing可以有很多重載。
二、通知鏈
1、編程方式實現。各種通知的實現代碼如下:
代碼
1 //前置通知
2 class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice
3 {
4 public void Before(MethodInfo method, object[] args, object target)
5 {
6 Console.WriteLine("前置通知執行");
7 Console.WriteLine("method name is {0}",method.Name);
8 if (args != null)
9 {
10 for (int i = 0; i < args.Length; i++)
11 {
12 Console.WriteLine(" the {0} position args is :{1}", i, args[i]);
13 }
14 }
15 Console.WriteLine("type of target is :{0}", target.GetType().ToString());
16
17 }
18 }
19
20 //環繞通知
21 class ConsoleLoggingAroundAdvice:IMethodInterceptor
22 {
23 public object Invoke(IMethodInvocation invocation)
24 {
25 object obj = null;
26 Console.WriteLine("環繞通知執行");
27 try
28 {
29 obj = invocation.Proceed();
30 }
31 catch
32 {
33 Console.WriteLine("目標方法執行異常");
34 }
35 return obj;
36 }
37 }
38
39 //后置通知
40 class ConsoleLoggingAfterAdvice : IAfterReturningAdvice
41 {
42 public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target)
43 {
44 Console.WriteLine("后置通知執行");
45 if (returnValue != null)
46 {
47 Console.WriteLine("returnValue is :{0}",returnValue);
48 }
49 Console.WriteLine("method name is :{0}", method.Name);
50 if (args != null)
51 {
52 for (int i = 0; i < args.Length; i++)
53 {
54 Console.WriteLine(" the {0} position args is :{1}",i,args[i]);
55 }
56 }
57 Console.WriteLine("type of target is :{0}",target.GetType().ToString());
58
59 }
60 }
61
62 //異常通知
63 class ConsoleLoggingThrowsAdvice:IThrowsAdvice
64 {
65 public void AfterThrowing(FormatException ex)
66 {
67 Console.WriteLine("發生異常通知");
68 Console.WriteLine(ex.Message);
69 }
70 }
代碼
1 ProxyFactory factory = new ProxyFactory(new ServiceCommand());
2
3 factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
4 factory.AddAdvice(new ConsoleLoggingAroundAdvice());
5 factory.AddAdvice(new ConsoleLoggingAfterAdvice());
6 factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
7
8 ICommand command = (ICommand)factory.GetProxy();
9 command.Execute("spring advice link ---programming");

注意織入方式,即factory.AddAdvice添加通知的順序。我個人感覺應該按照前置--環繞--后置的方式來進行織入。如果我改成
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
則運行結果如下圖:

2、配置方式實現。
配置文件如下:
代碼
1 <objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop">
2 <object id="myServiceObj" type="Spring.Aop.Framework.ProxyFactoryObject,Spring.Aop">
3 <property name="Target">
4 <object id="serviceCommand" type="SpringAdviceLink.ServiceCommand"></object>
5 </property>
6 <property name="InterceptorNames">
7 <list>
8 <value>beforAdvice</value>
9 <value>aroundAdvice</value>
10 <value>afterAdvice</value>
11 <value>exceptionAdvice</value>
12 </list>
13 </property>
14 </object>
15
16 <object id="beforAdvice" type="SpringAdviceLink.ConsoleLoggingBeforeAdvice"></object>
17 <object id="aroundAdvice" type="SpringAdviceLink.ConsoleLoggingAroundAdvice"></object>
18 <object id="afterAdvice" type="SpringAdviceLink.ConsoleLoggingAfterAdvice"></object>
19 <object id="exceptionAdvice" type="SpringAdviceLink.ConsoleLoggingThrowsAdvice"></object>
1 IApplicationContext context = ContextRegistry.GetContext();
2 ICommand command2 = (ICommand)context["myServiceObj"];
3 command2.Execute("spring advice link ---config");

在配置方式實現中,也要注意<property name="InterceptorNames"/>中list列表中的中值的順序,順序不一樣執行的結果也不一樣。我個人覺得應該是前置--環繞--后置的方式進行配置,至于異常通知的配置由于是連接點執行異常時才執行,所以它的為位置關系不重要。
總結:環繞通知實現的是AopAlliance.Intercept命名控件下的IMethodInterceptor接口,前置通知、后置通知、異常通知分別實現的是Spring.Aop命名空間下的IMethodBeforeAdvice、IAfterReturningAdvice、IThrowsAdvice接口.IThrowsAdvice的實現類必須有AfterThrowing方法,并且AfterThrowing可以有各種異常類型作為參數的重載。
參考文檔:Spring.Net參考框架.
代碼下載:代碼Demo
浙公網安備 33010602011771號