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