Spring.Net实现AOP以及AOP相关概念(学习笔记四)

  Spring的重要应用是在AOP(Aspect- Oriented programming面向方面编程)编程方面。Spring.Net是如何进行AOP的呢?下面是我在学习Spring实现AOP的例子以及我个人的一些浅显的理解,若有不妥之处,还望大侠们指出。

  本节重点分如下两部分:

  1、Spring实现AOP。

  2、AOP相关概念。

  先介绍如何实现AOP,然后再通过使用过程中的用法,谈谈我自己对AOP概念的理解。

  首先还是介绍一下开发环境以及软件版本:

  VS版本:VS2008 SP1、Spring版本:1.3.0。

  在我使用Spring.Net实现AOP的例子中有两个部分:1、使用编程方式实现。2、使用配置方式实现。实际在应用中,应用配置实现AOP还是相对更好点。实现AOP除了之前用到的程序集以外,还需使用程序集Spring.AOP。

  1、Spring实现AOP

  先说说编程实现的方式。这种方式当然不需要配置文件。主要应用了程序集Spring.Net中Spring.Aop.Framework命名空间下的ProxyFactory(代理工厂)类。

  先给出需要进行拦截的对象的代码:

 

代码
 
   
1 public interface ICommand
2 {
3 object Execute( object context);
4
5 }
6 public class BusinessCommand : ICommand
7 {
8 public object Execute( object context)
9 {
10 Console.WriteLine( " Service implementation is :{0} " , context);
11 return context;
12 }
13
14 }

  下面是实现对 BusinessCommand拦截的实现代码:

 

代码
 
   
1 public class ConsoleLoggingAroundAdvice : IMethodInterceptor
2 {
3 public object Invoke(IMethodInvocation invocation)
4 {
5 Console.WriteLine( " Method in ConsoleLoggingAroundAdvice " );
6
7 object obj = invocation.Proceed();
8 Console.WriteLine( " invocation type is :{0} " , invocation.GetType().ToString());
9 Console.WriteLine( " Method name is {0} " , invocation.Method.ToString());
10 return obj;
11 }
12 }
在上述拦截实现类中,我使用了环绕拦截。当然还有其他的拦截方式,以后会说到。我让 ConsoleLoggingAroundAdvice类继承了Spring.AOP程序集下的AopAlliance.Intercept命名控件中的 IMethodInterceptor,并中其中实现了接口的 object Invoke(IMethodInvocation invocation)方法。

  在这个例子中,我测试一下Spring.Net对Execute方法的拦截。使用代码如下:

代码
 
   
1 ICommand target = new BusinessCommand();
2 ProxyFactory factory = new ProxyFactory(target);
3 factory.AddAdvice( new ConsoleLoggingAroundAdvice());
4 object obj = factory.GetProxy();
5
6
7 ICommand business = (ICommand)obj;
8 Console.WriteLine(obj.GetType().ToString());
9 // ICommand command = (ICommand)factory.GetProxy();
10 business.Execute( " tyb " );
11 target.Execute( " This is the argument " );
说明: target.Execute( " This is the argument " );与拦截没有关系,即不会对 Execute方法进行拦截。后面我会说说为什么没有进行拦截的理解。

  输出如下图:


  从以上输出可以看到:我只是通过business.Execute("tyb")与 target.Execute("This is the argument")进行了输出。但是两条输出的执行方式是不一样的。显然后者输出的最后一条显示的结果。前者的输出结果为前5行。如果我用断点调试,则进行到ConsoleLoggingAroundAdvice中的Invoke方法。所以前一条输出了5行。为什么两条输出的结果会有这么大的差别呢?

  business、target两者都是实现了ICommand接口对象的实例,而前者是通过代理工厂(ProxyFactory)获取代理(GetProxy)的方式实现的,后者则不一样。我的理解是:

  通过factory.GetProxy()将拦截应用到了ICommand上,而target是直接通过new生成的,所以就没有将通知应用到target。

  在输出的第一行中,输出了object obj = factory.GetProxy()中obj的类型。它也可以转换为ICommand,说明它也是实现了ICommand的。那么如果我们将ICommand business = (ICommand)obj中将ICommand用实现它的类BusinessCommand类替换成BusinessCommand business = (BusinessCommand)obj,是不是就不能正常运行了呢?答案是肯定的。运行过程中跑出如下异常:

  由以上试验我们可推理:在应用拦截的对象中,拦截对象应该要实现一个接口。是不是这样呢。?我继续添加了ServiceCommand类,代码如下:

 

代码
 
   
1 public class ServiceCommand
2 {
3 public object Execute( object context)
4 {
5 Console.WriteLine( " Service implementation is :{0} " , context);
6 return context;
7 }
8 }
代码
 
   
1 ServiceCommand command2 = new ServiceCommand();
2 ProxyFactory factory2 = new ProxyFactory(command2);
3 factory2.AddAdvice( new ConsoleLoggingAroundAdvice());
4 ServiceCommand command3 = (ServiceCommand)factory2.GetProxy();
5 command3.Execute( " tyb " );

输出如下图:

 

  由以上输出,我可以得知,根本就没有进行拦截。

  注意:以上实现过程中,需要将接口申明为public类型的,即在给ProxyFactory的构造函数中给出对象的类型声明,不管是接口还是类对象,都必须是public的。

  接口为private类型时,在类型转换的时候会有如下异常:

  如果上例中的ServiceCommand未private类型的会有异常。但是和接口所抛出的不一样,有兴趣大家可以试试。

  说完了编程实现AOP,下面说说通过配置的方式实现。

  配置实现AOP。

  配置方式实现AOP通过配置的方式以及IOC容器获取对象的方式实现。在IOC容器的配置中,被拦截BusinessCommand类的定义是代理工厂对象(ProxyFactoryObject).

  配置如下:

 

代码
 
   
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 id = " businessCommand " type = " SpringAOPConfig.BusinessCommand " />
6 </ property >
7 < property name = " InterceptorNames " >
8 < list >
9 < value > consoleLoggingAroundAdvice </ value >
10 </ list >
11 </ property >
12 </ object >
13
14
15 < object id = " consoleLoggingAroundAdvice " type = " SpringAOPConfig.ConsoleLoggingAroundAdvice " />
16
17
18 </ objects >

其他接口定义以及类实现同编程实现的方式大致相同,只是在拦截类定义中,实现IMethodInterceptor接口中的Invoke(IMethodInvocation invocation)方法时,只调用了object obj = invocation.Proceed()。

 

  此时的使用方式如下:

 

代码
 
   
1 IApplicationContext context = ContextRegistry.GetContext();
2
3 // command2.Execute();
4  
5 object obj = context.GetObject( " myServiceObject " );
6 ICommand command = (ICommand)context.GetObject( " myServiceObject " );
7 command.Execute( " tyb " );
8
9 Console.WriteLine();
10
11 ICommand command2 = (ICommand)context[ " myServiceObject " ];
12 command2.Execute( " taoyongbo " );
13 // command.Execute();
14   IDictionary dic = context.GetObjectsOfType( typeof (ICommand));
15 foreach (DictionaryEntry item in dic)
16 {
17 Console.WriteLine( " key is: {0},value is :{1} " , item.Key, item.Value);
18 }

  首先过容器获取定义的对象后,通过类型转换后直接使用。

 

  输出结果如下图:

  这样就实现了和编程方式一样的效果。问题是上述的方式只是对ICommand中的Execute方法进行了拦截,如果还要对接口中的其他方法进行拦截怎么办呢。?

  我们只需在ICommand中定义另一个方法就行了。定义如下:

 

 
  
1 public interface ICommand
2 {
3 object Execute( object context);
4
5 object Execute();
6 }

  这样就实现了对接口中所有方法的拦截。

 

  假设我们还要将拦截应用到其他的类上,那么我们该如何实现呢。?新的接口定义与被拦截类的定义和第一个没什么区别,我这里就不在给出了,大家有兴趣可以下载代码了看看就行了。我主要给出配置文件。

 

代码
 
   
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 id = " businessCommand " type = " SpringAOPConfig.BusinessCommand " />
6 </ property >
7 < property name = " InterceptorNames " >
8 < list >
9 < value > consoleLoggingAroundAdvice </ value >
10 </ list >
11 </ property >
12 </ object >
13
14 < object id = " myServiceObject1 " type = " Spring.Aop.Framework.ProxyFactoryObject " >
15 < property name = " Target " >
16 < object type = " SpringAOPConfig.BusinessTest " />
17 </ property >
18 < property name = " InterceptorNames " >
19 < list >
20 < value > consoleLoggingAroundAdvice </ value >
21 </ list >
22 </ property >
23 </ object >
24
25 < object id = " consoleLoggingAroundAdvice " type = " SpringAOPConfig.ConsoleLoggingAroundAdvice " />

  由配置可以看出,新增的被拦截类的配置通前一个除了名称以外,其他都一样。

  使用方式如下:

代码
 
   
1 IApplicationContext context = ContextRegistry.GetContext();
2
3
4
5 object obj = context.GetObject( " myServiceObject " );
6 ICommand command = (ICommand)context.GetObject( " myServiceObject " );
7 command.Execute( " tyb " );
8 command.Execute();
9
10 Console.WriteLine();
11
12 ICommand command2 = (ICommand)context[ " myServiceObject " ];
13 command2.Execute( " taoyongbo " );
14 command2.Execute();
15
16 IDictionary dic = context.GetObjectsOfType( typeof (ICommand));
17 foreach (DictionaryEntry item in dic)
18 {
19 Console.WriteLine( " key is: {0},value is :{1} " , item.Key, item.Value);
20 }
21
22 // IApplicationContext ctx = ContextRegistry.GetContext();
23 // BusinessCommand command = (BusinessCommand)ctx["myServiceObject"];
24 // command.Execute();
25
26 // IApplicationContext context = ContextRegistry.GetContext();
27   ITest business = (ITest)context[ " myServiceObject1 " ];
28
29 business.Fun1();
30
31 Console.Read();
运行结果如下图:

  对配置文件的几点总结:

  一、在进行拦截时,都定义为由Spring.Aop.Framework.ProxyFactoryObject来处理。

  二、名称为Target的属性,定义为目标对象类型。

  三、属性为InterceptorNames的值,定义方面(Aspects)。

   当然,通过配置方式实现AOP时,也需要将接口声明为public,以及在获取到目标代理对象的时候,只能通过接口来进行类型转换,这点和编程实现AOP是一致的。

  2、AOP相关概念

  1、方面(Aspects):对横向分布在多个对象中的关注点所作的模块化。在企业应用中事务管理就是一个典型的横切关注点。在Spring.Net中,将方面实现为Advisor或拦截器(Interceptor)。Advisor一般是通知和切入点的组合。拦截器实际上就是指通知。Spring.Net中一般把环绕通知成为拦截器, 而其他类型的通知成为通知。因为环绕通知实现的是AopAlliance.Intercept.IMethodInterceptor接口,而其他类型通知实现的是Spring.AOP命名控件下的其他接口。如ConsoleLoggingAroundAdvice

  2、连接点(joinPoint):程序过程中的一个点。如程序中的方法。如:BusinessCommand中的Execute方法。

  3、通知(Advice):AOP框架在某个连接点(方法)中所采取的行为。通知有很多类型:如环线通知、前置通知、后置通知、异常通知。如:ConsoleLoggingAroundAdvice中public object Invoke(IMethodInvocation invocation)所执行的代码

  4、切入点(PointCut):指通知的应用条件,用于确定某个通知应用到那个连接点(方法)上。AOP框架允许程序员自己定义切入点,也可以在定义中使用正则表达式定义。如:object id="myServiceObject"节点的定义

  5、引入(Introduction):向目标对象添加字段或方法的行为。Spring.Net允许为目标对象引入新的接口。如:可以利用引入让任何对象在运行期间实现IAuditable接口,以简化对象状态变化的跟踪过程。如程序中使用:factory.AddAdvice(new ConsoleLoggingAroundAdvice())将通知应用到目标对象上。

  6、目标对象(Target Object ):包办连接点的对象。也被称为被通知或被代理对象。如:BusinessCommand

  7、AOP代理(AOP Proxy):由AOP框架在将通知应用于目标对象后创建的对象。在Spring.Net中是使用IL代码在运行期创建的动态代理。如:程序中使用的object obj = factory.GetProxy()中。

  8、织入(Weaving):将方面进行组装,以创建一个目标对象。它可以在编译期间完成,也可以在运行期完成。Spring在运行时执行织入。织如是一个过程,如程序中ProxyFactory factory = new ProxyFactory(target); factory.AddAdvice(new ConsoleLoggingAroundAdvice());    object obj = factory.GetProxy();的执行。

  以上就是我对Spring.Net中AOP的理解。要什么不妥指出,还望不吝赐教!

代码下载:Spring.Net AOP DEMO

参考文档:Spring.Net框架参考手册

       http://www.cnblogs.com/GoodHelper/archive/2009/11/13/SpringNet_Aop_Concept.html

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程通过实际项目融入常用开发技术架构,讲授风格独特,提供详细上课日志及答疑,赠送配套的项目架构源码注释详细清晰且表达通俗,均能直接在实际项目中应用,正真的物超所值,价格实惠任务作业:综合运用《C#/.Net企业级系统架构设计实战精讲教程》课程所学知识技能设计一个学生成绩管理系统的架构。要求:1.系统基于MVC的三层架构,各层单独建不同的解决方案文件夹。2.采用Model First开发方式,设计架构时只需要设计学生表(TbStudent)和课程表(TbCourse)。学生表必须有的字段是ID、stuName、age;课程表必须有的字段是ID、courseName、content。3.数据访问层采用Entity Framework或NHibernate来实现,必须封装对上述表的增删改查方法。4.必须依赖接口编程,也就是必须要有数据访问层的接口层、业务逻辑层的接口层等接口层。层层之间必须减少依赖,可以通过简单工厂或抽象工厂。5.至少采用简单工厂、抽象工厂、Spring.Net等技术中的2种来减少层与层之间的依赖等。6.封装出DbSession类,让它拥有所有Dal层实例和SaveChanges方法。7.设计出数据访问层及业务逻辑层主要类的T4模板,以便实体增加时自动生成相应的类。8.表现层要设计相关的控制器和视图来验证设计的系统架构代码的正确性,必须含有验证增删改查的方法。9.开发平台一定要是Visual Studio平台,采用C#开发语言,数据库为SQL Server。10.提交整个系统架构的源文件及生成的数据库文件。(注意: 作业需写在CSDN博客中,请把作业链接贴在评论区,老师会定期逐个批改~~)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值