第五章 面向方面编程___通知类型

  前面两节谈到了 AOP 的概念以及我们使用代理模式来模拟了 AOP ,在代理类中,我们对所有的方法进行了拦截,并没有做更细的处理。

Spring.Net 中帮我们提供了一套完善的 AOP 框架,对于目前绝大部分的需求都能够提供完整的支持。Spring.Net 中帮我们提供了多种对方法的拦截的方式,这种对方法进行拦截的方式专业术语又称 “通知” 。Spring.Net 的通知既可由某个类的所有对象共享,也可由该类型的单个实例独占。共享的通知称为基于类型(per-class)的通知,而独占的通知称为基于实例(per-instance)的通知。

  基于类型的通知最为常用。比如说事务,它就很适合用基于类型的通知实现。它们不依赖于目标对象的状态,也不会向目标对象添加新状态,仅仅对方法及其参数进行操作。

  基于实例的通知比较适合做引入 ( introductions ) 。此时通知可以向目标对象添加状态。在 AOP 代理中,可以同时使用基于类型和基于实例的通知。

  Spring.Net 帮我们提供了以下通知:

around advice(环绕通知):最常用的通知类型,又称为方法拦截器。环绕通知继承自 IMethodInterceptor 接口,在拦截到方法之后,运行程序员在方法调用之前或之后做操作。

before advise(前置通知): 前置通知只在方法调用之前执行,前置通知需要继承自 IMethodBeforeAdvice 接口。

after returning advise(后置通知): 后置通只在方法调用之后执行,后置通知需要继承自 IAfterReturningAdvice 接口,如果通知抛出异常,就会沿拦截器链向上抛出,从而中断拦截器链的继续执行。

throws advise(异常通知): 异常通知只在发生异常的情况下执行。

IMethodInterceptor 环绕通知接口:

IMethodBeforeAdvice  前置通知接口 : 

IAfterReturningAdvice  后置通知接口 :

IThrowsAdvice  异常通知接口 :

我们还是来上代码:

有一个银行卡的接口,其中有两个方法,存入 Deposit 和 支出 Pay

 1 namespace CnblogsLesson_5_2.Interface
 2 {
 3     public interface ICard
 4     {
 5         //存入
 6         void Deposit(double money);
 7 
 8         //支出
 9         void Pay(double money);
10     }
11 }

银行卡的实现类:

 1 using System;
 2 using CnblogsLesson_5_2.Interface;
 3 
 4 namespace CnblogsLesson_5_2.Impl
 5 {
 6     public class Card : ICard
 7     {
 8 
 9         /// <summary>
10         /// 存入
11         /// </summary>
12         public void Deposit(double money)
13         {
14             throw new Exception();
15             Console.WriteLine("存入{0}元", money);
16         }
17 
18         /// <summary>
19         /// 支出
20         /// </summary>
21         public void Pay(double money)
22         {
23             Console.WriteLine("支出{0}元",money);
24         }
25         
26     }
27 }

  Spring.Net 的非侵入性,决定了几乎所有的功能,都可以通过配置文件配置实现。而要实现 AOP 也是一样的,也可以通过配置实现。在使用 Spring.Net  AOP 功能的时候,需要对项目引入 Spring.Aop.dll 文件。废话不多说,我们来看一下怎么配置。我们现在要进行方法拦截,拦截到之后做一些事情。我们先得创建一些通知:

一 . 环绕通知

  如:环绕通知,我们在拦截到方法之后,可以做一些操作,比如在方法之前输出一句话或在方法之后做一些事情。我们现在来添加一个 AroundAdvice 类,如下:

 1 using AopAlliance.Intercept;
 2 using System;
 3 
 4 namespace CnblogsLesson_5_2.Notify
 5 {
 6     /// <summary>
 7     /// 环绕通知
 8     /// </summary>
 9     public class AroundAdvice : IMethodInterceptor
10     {
11         public object Invoke(IMethodInvocation invocation)
12         {
13             Console.WriteLine("我是环绕通知,参数是{0},我在调用执行方法之前做了一件事!",invocation.Arguments);
14             //执行方法
15             object result = invocation.Proceed();
16             Console.WriteLine("我是环绕通知,返回值是{0},我在调用执行方法之后做了一件事!",result);
17             return result;
18         }
19     }
20 }

执行程序:

 1 using Spring.Context;
 2 using Spring.Context.Support;
 3 using CnblogsLesson_5_2.Interface;
 4 
 5 namespace CnblogsLesson_5_2
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             IApplicationContext context = ContextRegistry.GetContext();
12 
13             ICard card = context.GetObject("card") as ICard;
14 
15             card.Pay(100);
16         }
17     }
18 }

看一下执行结果,可以看到,card 实例调用 Pay 方法 被拦截到的  环绕通知:

二 . 前置通知 

前置通知只在方法调用之前执行 :

 1 using Spring.Aop;
 2 using System;
 3 using System.Reflection;
 4 
 5 namespace CnblogsLesson_5_2.Notify
 6 {
 7     /// <summary>
 8     /// 方法前置通知
 9     /// </summary>
10     public class BeforeAdvice : IMethodBeforeAdvice
11     {
12         public void Before(MethodInfo method, object[] args, object target)
13         {
14             Console.WriteLine("方法前置通知调用:方法名称为" + method.Name + "。参数为" + args + "。目标对象:" + target);
15         }
16     }
17 }

看一下执行结果,可以看到,card 实例调用 Pay 方法 被拦截到的  前置通知:

三 . 后置通知

后置通只在方法调用之后执行,后置通知需要继承自IAfterReturningAdvice接口,如果通知抛出异常,就会沿拦截器链向上抛出,从而中断拦截器链的继续执行。

 1 using System;
 2 using Spring.Aop;
 3 
 4 namespace CnblogsLesson_5_2.Notify
 5 {
 6     public class AfterReturningAdvice : IAfterReturningAdvice
 7     {
 8         public void AfterReturning(object returnValue, System.Reflection.MethodInfo method, object[] args, object target)
 9         {
10             Console.WriteLine("开始调用方法后置通知:返回值为:" + returnValue + "。方法名称为:" + method.Name + "。参数是:" + args + "。目标对象是:" + target);
11         }
12     }
13 }

看一下执行结果,可以看到,card 实例调用 Pay 方法 被拦截到的 后置通知:

四 . 异常通知

异常通知只在发生异常的情况下执行,我们之前在 Deposit 方法中,手动抛出异常,为 Deposit 方法添加异常通知后,Deposit 方法执行过程中出现异常,将会被异常通知捕获到。

 1 using System;
 2 using CnblogsLesson_5_2.Interface;
 3 
 4 namespace CnblogsLesson_5_2.Impl
 5 {
 6     public class Card : ICard
 7     {
 8 
 9         /// <summary>
10         /// 存入
11         /// </summary>
12         public void Deposit(double money)
13         {
14             throw new Exception();
15             Console.WriteLine("存入{0}元", money);
16         }
17 
18         /// <summary>
19         /// 支出
20         /// </summary>
21         public void Pay(double money)
22         {
23             Console.WriteLine("支出{0}元",money);
24         }
25         
26     }
27 }

异常通知 类:

 1 using System;
 2 using Spring.Aop;
 3 
 4 namespace CnblogsLesson_5_2.Notify
 5 {
 6     /// <summary>
 7     /// 异常通知
 8     /// </summary>
 9     public class ThrowsAdvice : IThrowsAdvice
10     {
11         public void AfterThrowing(Exception ex)
12         {
13             Console.WriteLine("异常被触发了");
14         }
15     }
16 }

通过执行程序,可以到看,Deposit 方法手动抛出异常,被异常通知捕获到:

以上就是常用的四种通知类型,通过下面的配置文件,就可以知道 Spring.Net 中如何来配置它们:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <objects xmlns="http://www.springframework.net">
 3 
 4   <!--信用卡实例-->
 5   <object id="card" type="CnblogsLesson_5_2.Impl.Card,CnblogsLesson_5_2"/>
 6 
 7   <!--配置环绕通知-->
 8   <object id="aroundAdvice" type="CnblogsLesson_5_2.Notify.AroundAdvice, CnblogsLesson_5_2"></object>
 9   
10   <!--配置前置通知-->
11   <object id="beforeAdvice" type="CnblogsLesson_5_2.Notify.BeforeAdvice, CnblogsLesson_5_2"></object>
12   
13   <!--配置后置通知-->
14   <object id="afterReturningAdvice" type="CnblogsLesson_5_2.Notify.AfterReturningAdvice, CnblogsLesson_5_2"></object>
15   
16   <!--配置异常通知-->
17   <object id="throwsAdvice" type="CnblogsLesson_5_2.Notify.ThrowsAdvice, CnblogsLesson_5_2"></object>
18 
19   <!--配置AOP代理对象-->
20   <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
21     <!--代理的目标对象列表,如目前只代理了card对象-->
22     <property name="ObjectNames">
23       <list>
24         <!--这里可能需要代理的对象太多,Spring.Net  帮我们提供了通配符的匹配方式,如:"*name","name*",”*name*“和精确文本如"name"。而且还提供了正则表达式的匹配方式,这里就不举例了-->
25         <value>car*</value>
26       </list>
27     </property>
28     <!--AOP代理对象中,使用的通知实例-->
29     <property name="InterceptorNames">
30       <list>
31         <value>aroundAdvice</value>
32         <value>beforeAdvice</value>
33         <value>afterReturningAdvice</value>
34         <value>throwsAdvice</value>
35       </list>
36     </property>
37     
38   </object>
39   
40 </objects>

 

posted on 2013-03-25 14:15  何旭  阅读(1885)  评论(12编辑  收藏  举报