前面两节谈到了 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>