Enterprise_Library_Policy_Injection_Application_Block.docx_第1页
Enterprise_Library_Policy_Injection_Application_Block.docx_第2页
Enterprise_Library_Policy_Injection_Application_Block.docx_第3页
Enterprise_Library_Policy_Injection_Application_Block.docx_第4页
Enterprise_Library_Policy_Injection_Application_Block.docx_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

enterprise library policy injection application block之一在过去的半年里,定期或者不定期地写点东西已经成为了我的一种习惯。可是最近两个月来一直忙于工作的事情一直足够的时间留给自己,虽然给自己列了很长一串写作计划,可是心有余而力不足。这一段工作主要是帮助公司开发一套分布式的开发框架,对一些技术和设计方法有了一些新的认识。这两天的工作主要是如何把enterprise library v3.1的piab(policy injection application block)引入到我们自己的框架中,为次对piab进行了一些研究,借此机会与大家一起分享。 part i:piab overview part ii:piab设计和实现原理 part iii:piab扩展-如何创建custom callhandler 一、business logic 和 infrastructure logic的分离 对于任何一个企业级应用的开发人员来说,他们编写的代码不仅仅是处理单纯的业务逻辑,同时还需要处理很多的非业务方面的逻辑,比如:caching、transaction enlist、authorization、auditing、exception handling、logging、validation甚至是performance counter。我习惯把这些非业务逻辑成为infrastructure logic。由于这些infrastructure logic通常根据具体的需求注入的具体的业务逻辑的执行中,所以又被更多地成为crosscutting concern。 如果按照传统的oo的编程方式,我们通常将这些business concern和crosscutting concern糅合在一起,从而导致了某种意义的紧耦合。这种紧耦合在一个大型的企业级应用可能是会给应用的可扩展性带来致命的影响。所以必须以某种方式实现business concern和crosscutting concern的分离,这通常是aop实现的目标。 举个简单的例子,我们现在有如下一个简单的订单处理操作,可能具体的业务逻辑很简单。但是此操作面临的非业务逻辑可能有很多。比如:在处理之前进行authorization,确定当前的用户是否具有此操作的权限。 auditing,记录处理的用户、处理时间等等。 transaction enlist,将所有data access操作纳入同一个transaction,保证数据的一致性。 进行exception handling。 如果出现异常,记录日志。 上面的这些可以体现在线面的code中: public void processorder(order order) authorize(); audit(); using (transactionscope scope = new transactionscope() try orderdataaccess(); catch (exception ex) handleexceptoin(ex); log(); 可以看到上面这么多行代码中,和业务相关的就一行而已。虽然对这些非业务逻辑的实现通常通过调用一个封装好的方法或者组件完成,你往往只需要copy paste就可以了,但是将如此众多的infrastructure logic和business logic按照这样的方式组合在一起有很多的隐患。 首先,将一些公共的source code进行大规模的复制不能保证其同一性和正确性。我个人觉得,对于一个程序原来说,如果你频繁地使用到ctrl + c和ctrl + v,你应该想到你的代码需要重构。 其次,infrastructure logic和business logic这种耦合性导致对infrastructure logic的变动将获导致source code的改变。 此外,这样的编程方式增加了程序员的压力,可能对于一个相对junior的程序员来说,可能根本不知道这个infrastructure logic的具体作用何在,而实际上对于最终的程序员来讲,这些应该是对他们透明的。 所以我们需要寻求某种方式将这些infrastructure logic注入到某个具体的操作中,而不需要将所有的逻辑实现在具体的方法定义中。比如:你可以通过配置文件进行配置,你也可以通过添加attribute一声明的方式来实现。而这一切都可以通过enterprise library的piab来实现。二、piab(policy injection application block)overview piab在enterprise library 3.0中被引入(注意:不要把piab和diab混淆,diab-dependence injection application block将在enterprise library 4.0中被引入,个人觉得这将又是一个具有重大意义的ab,它的使用将会极大的减轻模块或组件的依赖关系,在software factory中已经有了广泛的应用)。piab提供了一种简单而有效的方式是你能够将你所需的非业务逻辑的crosscutting concern注入到的某个方法的invocation stack中。比如可以在方法的调用前注入auditing的执行,在成功调用后执行transaction commit的调用,在出现异常时进行exception handling的处理。 对于piab来说,policy的意思是:“将对应的处理操作注入到对应的方法调用”。这实际上包含两方面的内容:要注入怎样的处理操作和如何与具体的方法匹配。前者封装在一个称作callhandler的对象中,而匹配关系通过另一个称作matchingrule的对象来表现。所以可以说policy= callhandler+matchingrule。 piab给我们提供了很多系统自定义callhandler,在一般情况下它能满足我们绝大部分的需求,比如: enterprise library security application block的authorization来实现。 cachingcallhandler:将方法返回值作为value、参数列表作为key存入cache,如果下次出现相同参数列表的调用,则直接将cache中的值返回,从而免去了再次执行方法对性能的影响。像一般的cache处理一样,你也可以设置cache的过期时间。 exceptioncallhandler:通过调用enterprise library exception handing application block实现对一异常处理的支持。 logcallhandler:通过调用enterprise library logging application block进行日志的处理。 validationcallhandler:进行参数的验证等功能,该callhandler依赖于enterprise library validation application block。 performancecountercallhandler。 至于matchingrule,他实际上是定义了一种如何将callhander和对应的method进行匹配的规则。同样的,一系列的matchingrule被定义出来,比如:基于assembly的matchingrule将callhandler匹配到某个assembly的所有对象上面;基于custom attribute的matchingrule通过声明在某个元素上的某个attribute进行匹配。此外还有基于namespace、method signature、等等的matchingrule。 如何现有的callhandler和matchingrule不能满足你具体的要求,你还可以定义custom callhandler或者 custom matchingrule。在后续的部分将会介绍一个完整的定义custom callhandler的例子。三、get started 经过上面对piab的介绍,我想大家对piab使用的目标又一个简单的认识,为了加深大家的映像,在本节中,我们做一个简单的walkthrough,做一个简单的使用piab的例子。在这个例子通过piab实现两个主要的功能:caching和logging。为了简单起见,我们使用一个console application来实现这样一个例子。我们假设的一个场景时处理一个订单并将处理后的订单返回。 step i: 创建一个console application,并添加下面连个dll reference. microsoft.practices.enterpriselibrary.policyinjection microsoft.practices.enterpriselibrary.policyinjection.callhandlers step ii:编写order processing的代码: public class orderprocessor:marshalbyrefobject public order process(order order) console.writeline(orderprocessor.process() is invocated!); return order; public class order 至于为什么要继承marshalbyrefobject将会在后面对piab的实现原理中介绍。将对orderprocessor的调用写在main()。 static void main(string args) orderprocessor processor = policyinjection.create(); order order = new order(); processor.process(order); processor.process(order); step iii:创建app.config,并通过enterprise library configuration console进行配置: 1、添加一个logging application block 2、按照相同的方式添加一个policy injection application block,然后再piab节点添加一个policy。在该policy下的match rules节点下创建一个method name matching rule (该matchingrule根据method name进行callhandler的匹配)。对该matchingrule进行如下的配置: 那么该policy的callhandler将会自动应用到method name为process的方法上。3、在policy下的handlers节点添加两个callhandler:caching handler和logging handler。保持caching handler的默认配置,按如下进行loging handler的配置: 通过上面的配置,我们将会得到如下的configuration: configuration 我们现在来运行以下程序: 从输出的结果可以看出,虽然我们在main()两次调用了process方法,但是真正执行的数次只有一次。这是caching所致,第一次调用piab将返回值存入cache,该cache entry的key为参数order对象。第二次调用由于传入的参数相同,所有直接将上次的结果返回。 上面我看到了caching的效果,现在我们在看看logging。默认情况下,logging entry被写入event log。下图就是我们写入的log。 我们现在来运行以下程序: 从输出的结果可以看出,虽然我们在main()两次调用了process方法,但是真正执行的数次只有一次。这是caching所致,第一次调用piab将返回值存入cache,该cache entry的key为参数order对象。第二次调用由于传入的参数相同,所有直接将上次的结果返回。 上面我看到了caching的效果,现在我们在看看logging。默认情况下,logging entry被写入event log。下图就是我们写入的log。 enterprise library policy injection application block之二在前面一篇文章中,我对enterprise library中的piab (policy injection application block)作了简单的介绍。在这篇文章主要谈谈我个人对piab设计和实现原理的一些理解。在介绍过程中,我尽量采用由浅入深出的方式,同时结合例子、source code。希望通过本篇文章让大家对piab有一个全面、深刻的认识。 一、mbr、objref、realproxy、transparentproxy 在真正进入piab之前,我们现来谈论一些与之相关的、必要的背景知识。mbr、objref、realproxy和transparentproxy,对于这些名词,我想熟悉或者接触过.net remoting的人肯定不会不陌生。由于piab的实现机制依赖于remoting的这种marshaling,如果对此不了解的读者将对后面的介绍很难理解,所以很有必要先做以下简单的介绍。 我们知道,clr通过appdomain实现不同application之间的隔离,在通常情况下,不同appdomain不同共享内存。在一个appdomain中创建的对象不能直接被运行在另一个appdomain的程序使用。跨appdomain对象的共享依赖于一个重要的过程:marshaling。我们有两种不同的marshaling方式:marshaling by value和marshaling by reference。前者采用的是serialization/deserialization的方式,而后者则是采用传递reference的方式来实现,其实现原来如下: remoting infrastructure先生成对象的objref instance,objref(system.runtime.remoting.objref)代表远程对象的一个reference,存储着跨appdomain远程调用所需的所有的信息,比如uri、类型的层次结构、以及实现的interface等等。objref是可以序列化的,也就是说它可以按照by value的方式进行marshaling。所以可以这么说marshaling by reference依赖对对objref的marshaling by value。 当objref产地到另一个appdomain的实现,将根据objref的数据生成两个proxy对象:realproxy和transparentproxy。realproxy根据objref创建,通过realproxy创建transparentproxy。当进行远程调用的时候,client直接和transparentproxy打交道,对transparentproxy的调用将会forward到realproxy,realproxy通过remoting infrastructure的communicate和activation机制将invocate传递给被激活的remote object。 mbr通常在一个跨appdomain的环境下实现远程调用,但是这种机制在用一个appdomian依然有效,而且可以免去跨appdomain的性能损失。piab就是充分运用了这种在同一个appdomain下的mbr。 二、method interception & custom realproxy 在第一部分我们知道,piab的实现是通过将policy应用到对应的method上,在真正执行目标对象具体的方法的之前,piab将整个方法的调用拦截,然后逐个调用应在该method上的policy包含的所有callhandler(在前一章我们说过policy = matching rule + call handler),最后再调用真正目标对象的方法。我们把这种机制成为method injection。 如何才有实现这样的method injection呢?这就要需要使用到我们在上面一节介绍的mbr了。通过上面的介绍,我们知道我们调用一个mbr object的过程是这样的: client code=transparent proxy=real proxy=target object 在上面的invocate chain中,由于真正的目标对象的方法在最后一部才被调用,我们完全有可以在中途将调用”劫持”,使之先调用我们的callhandler。而这种inject的突破口在于realproxy。在fcl(framework class library)中realproxy(system.runtime.remoting.proxies.realproxy)是个特殊的abstract class,你可以继承realproxy定义你自己的custom realproxy,将你需要注入的操作写在invoke方法中。piab采用的就是这样一种解决方案。 我们先不忙介绍piab的具体的实现原理,因为相对比较复杂。为了使读者能够更加容易地理解piab的实现,我写了一个简单的例子。我们它能够大体体现piab的实现机制。 这是一个简单的console application,我首先定义了一个custom realproxy: public class myrealproxy:realproxy private t _target; public myrealproxy(t target) : base(typeof(t) this._target = target; public override imessage invoke(imessage msg) /invoke injected pre-operation. console.writeline(the injected pre-operation is invoked); /invoke the real target instance. imethodcallmessage callmessage = (imethodcallmessage)msg; object returnvalue = callmessage.methodbase.invoke(this._target, callmessage.args); /invoke the injected post-operation. console.writeline(the injected post-peration is executed); /return return new returnmessage(returnvalue, new object0, 0, null, callmessage); 这是一个generic的realproxy。在invoke方法中,两个console.write()代表piab注入的callhandler的调用(对于callhandler的操作可以是在调用target object之前调用,也可以在之后调用,我们不妨称这两种类型的操作为pre-operation和post-op eration)。而对target object的调用实际上是通过reflection的方式调用的(callmessage.methodbase.invoke)。myrealproxy通过传入target instance来创建。 我们在创建一个factory class,用于创建transparentproxy。在piab中,这样一个class由microsoft.practices.enterpriselibrary.policyinjection.policyinjection来充当。 public static class policyinjectionfactory public static t create() t instance = activator.createinstance(); myrealproxy realproxy = new myrealproxy(instance); t transparentproxy = (t)realproxy.gettransparentproxy(); return transparentproxy; 先通过reflection的方式来创建target instance。通过该instance创建我们在上面定义的custom realproxy。最后通过realproxy返回一个transparentproxy。 有了上面两个class,我们的编写如下的code来验证我们自己的method injection: public class foo:marshalbyrefobject public voiddosomething() console.writeline(the method of target object is invoked!); public class program public static void main() foo foo = policyinjectionfactory.create(); foo.dosomething(); 我们来看看程序运行后的结果: 可以看到正式我们需要的结果。从这个例子中我们可以看到,我们的client code中包含的仅仅是business logic相关的code, 而另外一些业务无关的code则是通过custom realproxy的形式注入到invocation stack中。这充分体现了business concern和crosscutting concern的分离。三、call handler pipeline 我想有了上面的理论和例子作基础,大家对于piab真正的实现不难理解了。我们先来介绍一下pi一个重要的概念:callhandler pipeline。我们知道policy被运用method方面,一个policy有多个callhandler。所有一个method往往关联着一连串的callhandler。这种现在很常见,就上我们第一部分给出的例子一样,一个简单的processorder方面需要执行许多额外的业务无关的逻辑:authorization、auditing、transaction enlist、exception handling和logging。 在piab中,这些基于某个method的所有callhandler逐个串联在一起,组成一个callhandler pipeline。具体情况如下图: 我们从class diagram的角度来认识callhandler pipeline(在piab中通过microsoft.practices.enterpriselibrary.policyinjection.handlerpipeline来表示)。 public delegate imethodreturninvokehandlerdelegate(imethodinvocation input, getnexthandlerdelegate getnext); public interface imethodinvocation / methods imethodreturncreateexceptionmethodreturn(exception ex); imethodreturncreatemethodreturn(object returnvalue, params object outputs); / properties iparametercollectionarguments get; iparametercollectioninputs get; idictionaryinvocationcontext get; methodbasemethodbase get; objecttarget get; public delegate invokehandlerdelegate getnexthandlerdelegate(); public interface icallhandler / methods imethodreturninvoke(imethodinvocation input, getnexthandlerdelegate getnext); imethodinvocation代表对一个方法的调用,它封装了一个method invocation的相关信息。比如:parameter list、invocation context和target object.invokehandlerdelegate代表的是对callhandler的调用,由于所有的callhandler被串成一个callhandler pipeline,在调用了当前callhandler之后,需要调用pipeline中后一个callhandler,对后一个callhandler调用通过一个delegate,getnexthandlerdelegate来表示,而该delegate的返回值是invokehandlerdelegate。结合上面的callhandler pipeline的链状结构,对这些应该不难理解。 我们最后来看看handlerpipeline的定义,它的所有的callhandler通过一个list来表示。 public class handlerpipeline / fields private list handlers; / methods public handlerpipeline(); public handlerpipeline(ienumerable handlers); public imethodreturninvoke(imethodinvocation input, invokehandlerdelegate target); handlerpipeline通过invoke开始对其callhandler pipeline的调用: public imethodreturn invoke(imethodinvocation input, invokehandlerdelegate target) if (this.handlers.count = 0) return target(input, null); int handlerindex = 0; return this.handlers0.invoke(input, delegate handlerindex+; if (handlerindex this.handlers.count) icallhandler local1 = this.handlershandlerindex; return new invokehandlerdelegate(local1.invoke); return target; ); 逻辑并不复杂,按照callhandler list的先后顺序逐个调用,最后调用target object。方法中的第二个参数代表target代表对target object的调用。 四、piab中的custom realproxy:interceptingrealproxy 我们一直再强调,piab实际上是通过自定义realproxy来实现的。而且在第二节我们也实验了这种做法的可行性。我们现在就来看看piab的custom realproxy:microsoft.practices.enterpriselibrary.policyinjection.remotinginterception.interceptingrealproxy。 public class interceptingrealproxy : realproxy, iremotingtypeinfo / fields private dictionary memberhandlers; private readonly object target; private string typename; / methods public interceptingrealproxy(object target, type classtoproxy, policyset policies); private void addhandlersforinterface(type targettype, type itf); private void addhandlersfortype(type classtoproxy, policyset policies); public bool cancastto(type fromtype, object o); public override imessage invoke(imessage msg); / properties public object target get; public string typename get; set; 上面是它所有的成员定义,其中memberhandlers是一个以methodbase为key的dictionary,表示应用在target object上面的所有的callhandler pipeline。通过这个很容易获得某个具体的方法调用的pipeline。而target值得是真正的target object。 我们着重来看看invoke的定义: public override imessage invoke(imessage msg) handlerpipeline pipeline; imethodcallmessage callmessage = (imethodcallmessage) msg; if (this.memberhandlers.containskey(callmessage.methodbase) pipeline = this.memberhandlerscallmess

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论