一起谈.NET技术,IoC+AOP的简单实现

  对EnterLib有所了解的人应该知道,其中有一个名叫Policy Injection的AOP框架;而整个EnterLib完全建立在另一个叫作Unity的底层框架之上,我们可以将Unity看成是一个IoC的框架。对于一个企业应用来说说,AOP和IoC是我们进行逻辑分离和降低耦合度最主要的方式,而将两者结合起来具有重要的现实意义。

  一、基于IoC+AOP的编程

  到底将IoC和AOP进行整合后,会对编程但来怎样的影响,我写了一个简单的例子(你可以从这里下载该实例)。假设我现在有两个模块,分别称为Foo和Bar,通过如下同名的类来表示。Foo和Bar具有各自的接口,分别为IFoo和IBar。简单起见,我在两个接口中定义了相同的方法:DoSomething。在Foo中,具有一个类型为IBar的只读属性,而DoSomething的实现就是通过调用该属性的同名方法实现。

   1: public interface IFoo
   2: {
   3:     void DoSomething();
   4: }
   5:    
   6: public interface IBar
   7: {
   8:     void DoSomething();
   9: }    
  10: [FooCallHandler]
  11: public class Foo : IFoo
  12: {
  13:     public IBar Bar { get; private set; }
  14:     public Foo(IBar bar)
  15:     { this.Bar = bar; }
  16:     public void DoSomething()
  17:     {
  18:         this.Bar.DoSomething();
  19:     }
  20: }
  21: [BarCallHandler]
  22: public class Bar : IBar
  23: {
  24:     public void DoSomething()
  25:     {
  26:         Console.WriteLine("Do something...");
  27:     }
  28: }

  在类型Foo和Bar上分别应用了两个自定义特性:FooCallHandlerAttribute和BarCallHandlerAttribute,熟悉PIAB的读者对此应该不感到陌生——CallHandler承载着被分离出来的横切关注点(Crosscutting Concern)的实现。由于在这里仅仅是一个简单的演示,我定义了两个最简单的CallHandler:FooCallHandler和BarCallHandler。下面是这个两个CallHandler的对应的HandlerAttribute的定义。

   1: public class FooCallHandler : ICallHandler
   2: {
   3:     public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
   4:     {
   5:         Console.WriteLine("Foo: Preoperation is executed.");
   6:         var methodReturn = getNext()(input, getNext);
   7:         Console.WriteLine("Foo: Postoperation is executed.");
   8:         return methodReturn;
   9:     }
  10:     public int Order { get; set; }
  11: }
  12: public class BarCallHandler : ICallHandler
  13: {
  14:     public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  15:     {
  16:         Console.WriteLine("Bar: Preoperation is executed.");
  17:         var methodReturn = getNext()(input, getNext);
  18:         Console.WriteLine("Bar: Postoperation is executed.");
  19:         return methodReturn;
  20:     }
  21:     public int Order { get; set; }
  22: }
  23: public class FooCallHandlerAttribute : HandlerAttribute
  24: {
  25:     public override ICallHandler CreateHandler(IUnityContainer container)
  26:     {
  27:         return new FooCallHandler { Order = this.Order };
  28:     }
  29: }
  30: public class BarCallHandlerAttribute : HandlerAttribute
  31: {
  32:     public override ICallHandler CreateHandler(IUnityContainer container)
  33:     {
  34:         return new BarCallHandler { Order = this.Order };
  35:     }
  36: }

  两个CallHandler具有相似的实现:在目标方法执行前后打印相应的文字,代表在对方法调用进行拦截后,需要在方法执行前后实现的横切关注点。目前为止,提供具体功能的组建定义完成,我们来编写消费它们的客户程序:

   1: static void Main(string[] args)
   2: {
   3:     var container = GetContainer();           
   4:     var foo = container.Resolve<IFoo>();
   5:     foo.DoSomething();
   6: }

  从上面我们可以看出,我们最终的代码仅仅只要三行。虽然简单,我们不妨来也做一下分析:首先,客户端对组件Foo的调用是基于接口(IFoo)而不是基于具体类型Foo,这样能够尽可能地降低对组件Foo的依赖;其次,Foo依赖于Bar,而这种依赖也是基于接口的,这在Foo的定义了看得出来;第三,虽然Foo依赖于Bar,但是这种依赖是和Foo的具体实现相关,和客户程序无关,所以客户程序未曾出现Bar和IBar的影子。

  运行上面的程序,你会得到如下的输出。从中我们可以看出:不但具体的业务功能(即定义在Bar的DoSomething方法中的逻辑)能够正常执行,通过自定义特性的方式应用到两个组件上动态注入的横切关注点也得到正确地执行。

   1: Foo: Preoperation is executed.
   2: Bar: Preoperation is executed.
   3: Do something...
   4: Bar: Postoperation is executed.
   5: Foo: Postoperation is executed.

  那么,是什么导致了程序的完全按照我们希望的方式执行的呢?由于客户端逻辑只有三句代码,你肯定会猜的到:所有的一切都是由一开始创建的container对象完成的。如何你了解Unity的话,应该可以猜出这是一个UnityContainer。通过接口和类型的匹配关系的注册,UnityContainer知道如何根据接口找到相应的实现类型(IFoo-〉Foo,IBar-〉Bar),这不难理解,这也不是本篇文章介绍的重点。我们关注点是:UnityContainer是如何让通过自定义特性方式应用在Foo和Bar上的两个CallHandler得到执行的?

  二、CallHandler是如何被执行的

  同所有的AOP实现一样,PIAB也是采用方法拦截(Method Interception)机制。具体来说,PIAB又具有两个不同的方式:实例拦截(Instance Interception)和类型拦截(Type Interception)。而前者具有两种不同的实现:TransparentProxy/RealProxy的方式和Reflection Emit的方式。三种不同拦截方式的具体实现,不是本文的重点,对此有兴趣的朋友可以参阅PIAB官方文档。

  总的来说,能够在方法执行过程中被拦截的对象需要通过PIAB的方式进行创建,实际上PIAB是对目标对象进行相应的封装,进而得到一个代理对象。由于方法调用请求是从代理对象发出,PIAB在将该请求发送给最终的目标对象之前将其拦截下来,从而使相应的CallHandler得以执行。那么,如果我们能够让UnityContainer在对象创建过程完成这一道工序,那么最终被创建出来的对象就具有了被拦截的能力。

  如何将PIAB对实例的封装操作注入到UnityContainer怎个对象创建流程中呢?这需要借助于UnityContainer提供的扩展机制。虽然Unity仅仅是一个轻量级的IoC框架,但是内部的实现其实是挺复杂的。个人曾经不止一次地分析过Unity的源代码,但是没过多久就忘得七七八八。不过,万变不离其宗,UnityContainer最根本的就是其BuilderStrategy管道(可以参阅我的文章《你知道Unity IoC Container是如何创建对象的吗?》)。

  我们的解决方案就是将PIAB对实例的封装写在相应的BuilderStrategy种,然后通过UnityContainerExtension注册到某个UnityContainer中。为此我创建了如下一个名称为InterceptionStrategy的BuilderStrategy。

   1: public class InterceptionStrategy : BuilderStrategy
   2: {
   3:     public static IUnityContainer UnityContainer { get; private set; }
   4:     public static InjectionMember InterceptionMember { get; private set; }
   5:  
   6:     static InterceptionStrategy()
   7:     {
   8:         IUnityContainer container = new UnityContainer();
   9:         UnityContainerConfigurator configurator = new UnityContainerConfigurator(container);
  10:         EnterpriseLibraryContainer.ConfigureContainer(configurator, ConfigurationSourceFactory.Create());
  11:         UnityContainer = container;
  12:         InterceptionMember = new InstanceInterceptionPolicySettingInjectionMember(new TransparentProxyInterceptor());
  13:     }
  14:  
  15:     public override void PostBuildUp(IBuilderContext context)
  16:     {
  17:         if (null == context.Existing ||
  18:             context.Existing.GetType().FullName.StartsWith("Microsoft.Practices") ||
  19:             context.Existing is IInterceptingProxy)
  20:         {
  21:             return;
  22:         }
  23:         context.Existing = UnityContainer.Configure<TransientPolicyBuildUpExtension>().BuildUp
  24:             (context.OriginalBuildKey.Type, context.Existing, null, InterceptionMember);
  25:     }
  26: }

  如果你对Unity的内部机制有一定了解,理解上面的实现应该不成问题,但是你对此一无所知,我讲解得再详细你可能也弄不清楚。所以,对于上述的逻辑实现,在这里就不多作介绍了。

  自定义的BuilderStrategy一般需要通过相应的UnityContainerExtension完成注册。下面是我们创建的UnityContainerExtension:InterceptionExtension。我们将UnityContainerExtension对象添加到UnityContainer的BuilderStrategy管道之中,相应的BuilderStage设置为PreCreation。

   1: public class InterceptionExtension: UnityContainerExtension
   2: {
   3:     protected override void Initialize()
   4:     {
   5:         Context.Strategies.AddNew<InterceptionStrategy>(UnityBuildStage.PreCreation);
   6:     }
   7: }

  三、GetContainer()的实现…

  最初例子得以正常运行的魔力来自于通过静态方法GetContainer创建的UnityContainer对象,我们现在来看看该方法的实现。首先我们创建一个UnityContainer对象,然后对其进行初始化配置,最后将上面创建的InterceptionExtension扩展添加到该UnityContainer中。接口和实现类型的注册被随后执行,不过在真正的开发中,我们习惯通过配置文件进行注册。这就是整个实现,没有复杂的逻辑,却能带来很大的用处。

   1: static IUnityContainer GetContainer()
   2: {
   3:     IUnityContainer container = new UnityContainer();
   4:     UnityContainerConfigurator configurator = new UnityContainerConfigurator(container);
   5:     EnterpriseLibraryContainer.ConfigureContainer(configurator, ConfigurationSourceFactory.Create());
   6:     container.AddNewExtension<InterceptionExtension>();
   7:     container.RegisterType<IFoo, Foo>();
   8:     container.RegisterType<IBar, Bar>();
   9:     return container;
  10: }

  四、申明

  文中提供解决方案只是为你提供一种思路,相关的逻辑实现基本来自于脑子中的灵光一现,并没有进行深入的评估和性能测试。如果你希望在你自己的项目中使用,最好在此基础上进行深入的思考,相信会发现其中存在的不足。此外,不知道读者有没有注意,上面的实现方式仅仅提供一种拦截方式,即基于TransparentProxy的方式,有兴趣的读者可以再作一些扩展,实现对拦截方式的定制。

时间: 2024-10-30 02:34:34

一起谈.NET技术,IoC+AOP的简单实现的相关文章

IoC+AOP的简单实现

对EnterLib有所了解的人应该知道,其中有一个名叫Policy Injection的AOP框架:而整个EnterLib完全建立在另一个叫作Unity的底层框架之上,我们可以将Unity看成是一个IoC的框架.对于一个企业应用来说说,AOP和IoC是我们进行逻辑分离和降低耦合度最主要的方式,而将两者结合起来具有重要的现实意义. 一.基于IoC+AOP的编程 到底将IoC和AOP进行整合后,会对编程但来怎样的影响,我写了一个简单的例子(你可以从这里下载该实例).假设我现在有两个模块,分别称为Fo

一起谈.NET技术,一种简单的直观的高效的权限设计

大部分系统都有权限系统.一般来说,它能管控人员对某个否页面的访问:对某些字段.控件可见或者不可见.对gridview中的数据是否可删除.可添加.可新增等等.大部分人都把权限作为一个子系统独立出来.但是这里我不是想设计一个权限管理系统,网上的设计方案太多了,可以说每个开发人员都有自己的开发权限管理系统的想法和思路. 在这篇文章中,我先用简单的C#代码模仿一个用户的权限,再使用sql去模拟.这是一种很简单,很直观,很高效的方式去判定用户的权限. C#: 好吧,先从最简单开始,定义一个用户(User)

php基于jquery的ajax技术传递json数据简单实例_php技巧

本文实例讲述了php基于jquery的ajax技术传递json数据简单实现方法.分享给大家供大家参考,具体如下: html页面: <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <script type="text/javascript" src="jquery-1.8.2.mi

《创业家》牛文文:少谈点模式多谈点技术

"模式"如同当年的"主义",流行于各种创业大赛.创业励志节目.论坛的"街头"式秀场 文/创业家 牛文文 "美国某某公司你知道吧?就是刚被戴尔.惠普.思科十几亿美元抢购的那家.我们的模式和它的一样,现在还没赢利,可将来起码有十几亿人民币的市值." "我开了小煤矿,但煤运不出去,上商学院之后受到启发,想搞模式创新,具体讲就是想在铁路边上搞个煤炭物流开发区,建一个大的物流和信息流平台,把分散的煤炭集中在我这个园区,这样和铁

一起谈.NET技术,.Net语言中关于AOP 的实现详解

文章主要和大家讲解开发应用系统时在.Net语言中关于AOP 的实现.LogAspect完成的功能主要是将Advice与业务对象的方法建立映射,并将其添加到Advice集合中.由于我们在AOP实现中,利用了xml配置文件来配置PointCut,因此对于所有Aspect而言,这些操作都是相同的,只要定义了正确的配置文件,将其读入即可.对于Aspect的SyncProcessMessage(),由于拦截和织入的方法是一样的,不同的只是Advice的逻辑而已,因此在所有Aspect的公共基类中已经提供了

一起谈.NET技术,关于C#线程,线程池和并行运算的简单使用和对比

前言:看了书上两个使用C#4.0并行编程的demo,又对照以前收藏的网上几篇讲述线程池的雄文,一并整理,写个示例总结一下.写这篇文章的时候,发现关于线程的好几个基础的重要的知识点自己都不熟悉,而且可能习惯性认知浅薄,所以痛苦的无以复加,不知道到底要说什么.不想看文章的可以直接下载最后的示例,本文代码主要参考Marc Clifton的".NET's ThreadPool Class - Behind The Scenes",对新手也许有帮助. 参考: http://msdn.micros

一起谈.NET技术,ASP.NET MVC 2生成动态表单的一种最简单的思路

在BPM.OA等系统中,都会存在一个表单设计器.有些是通过操作gridview来完成一个表单的设计:有些是通过类似VS拖拽的方法完成一个表单的设计.很明显后面一种优越于前面一种.无论是哪种,最后都会产生一些XML之类的表单结构的数据. 这篇文章将讲述,在表单设计器设计好表单之后,在ASP.NET MVC中如何将表单结构的xml转换成实际应用系统中的表单.看下面一个xml文件,我们假设它是由一个表单设计器设计出来的. <?xml version="1.0" encoding=&qu

一起谈.NET技术,WCF+WF双剑合璧构建微软的SOA系列(一):从一个简单的Demo开始

本系列文章将从实例出发,以实例结尾.由浅入深讲解在我们项目中如何使用WCF和WF.我们会发现使用WCF+WF将造就出其他技术无法达到的高度.最后我会将程序架到云端. 微软.net的3W(WPF.WCF.WF)战略如下图.WCF负责通信,WPF负责界面展示,WF负责处理业务逻辑,如下图. 本系列文章会主要用到上图中的所有技术,但是主要讲述如何使用WCF和WF来实现系统的中间层.看过亮剑的朋友知道李云龙常打胜仗,并不是他懂得很多很多的战争的理论知识,而是来自实战中的经验.所以本系列的文章以实战为核心

一起谈.NET技术,利用AOP重构代码

AOP是什么? AOP是OOP的延续,Aspect Oriented Programming的缩写,即面向方面编程.AOP是GoF设计模式的延续,设计模式追求的是调用者和被调用者之间的解耦,AOP也是这种目标的一 种实现. 案例:在应用程序中,我们经常会对某一段程序做异常处理,或者是把一个方法的调用所消耗的时间体现在日志中,如果我们对每个方法都写具体的实现,我想并不是一件轻松的事情.对于异常处理来讲,其实我们平常编程很少去捕获具体的异常,当然特殊程序除外,例如客户端捕获WCF异常时最好捕获Com