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

  前面两节谈到了 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   何旭  阅读(1887)  评论(12编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
历史上的今天:
2012-03-25 C#正则表达式
< 2013年3月 >
24 25 26 27 28 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 1 2 3 4 5 6

统计

点击右上角即可分享
微信分享提示