配色: 字号:
再谈PIAB与Unity之间的集成
2016-10-08 | 阅:  转:  |  分享 
  
再谈PIAB与Unity之间的集成

在EnteLib中,PIAB(PolicyInjectionApplicationBlock)和Unity的定位是轻量级的AOP框架和IoC容器(Container)。通过PIAB,我们可以将一些业务无关的crosscuttingconcern定义于相应的CallHandler中,通过Attribute声明或者配置应用到承载业务逻辑的目标方法上。而通过Unity提供的IoC容器(或者DI容器),即UnityContainer,很好地实现了依赖的动态注入,从而实现了组件之间、模块之间或者服务之间的松耦合。



Unity完全建立在ObjectBuilder2之上,顾名思义,这是一个用于创建对象的基础组件。ObjectBuilder2提供了一种具有高可扩展性的、基于策略(StrategyBased)的对象创建框架,它不仅仅是Unity的基础组件,也是整个EnterLib和SoftwareFactory的基石。而PIAB通过方法调用劫持(MethodCallInterception)的机制实现了策略注入(PolicyInjection)。PIAB提供了不同的方法劫持机制,最为典型的就是基于TransparentProxy(可以参考我的PIAB系列文章)和代码生成(比如动态生成一个继承自目标类型的子类,通过Override掉相应的Virtual方法实现策略注入;或者动态生成一个实现了目标接口的类型,实现相应的方法实现策略注入)。PIAB需要通过特殊的机制创建可被劫持(Interceptable)对象,而UnityContainer本质上是一个创建对象的容器,如果能够使UnityContainer按照PIAB的要求创建可被劫持(Interceptable)对象,那么就能实现两者之间的集成。(SourceCode从这里下载)



一、Unity1.2和EnterLib4.1如何实现两者的集成



我在本系列的第一篇文章就谈过PIAB和Unity之间的集成问题,当时我们是采用了一个自定以UnityContainerExtension实现的,当时针对的版本是Unity1.1和EnterLib3.1。到了Unity1.2和EnterLib4.1,Unity已经被广泛地使用到了整个EnterLib内部,微软甚至通过Unity对PIAB进行了彻底的改造。所以,最新的Unity和PIAB中,已经提供了两者的原生集成。



Unity和PIAB两者之间的集成是通过一个特殊的UnityContainerExtension——Microsoft.Practices.Unity.InterceptionExtension.Interception实现的。为了演示Interception的使用,我们创建一个简单的例子。该例子中定义了一服务SyncTimeProvisionService用于实现同步时间的提供,SyncTimeProvisionService实现了接口ISyncTimeProvision。SyncTimeProvisionService本身并不提供具体实现,而是通过另一个组件SyncTimeProvider实现具体的同步时间的返回,SyncTimeProvider实现接口ISyncTimeProvider。



你可以将SyncTimeProvisionService和SyncTimeProvider看成是一个应用中具有依赖关系的两个模块,为了实现两个模块之间的解耦,采用基于接口的依赖是推荐的设计模式。所以,SyncTimeProvisionService并不之间依赖于SyncTimeProvider,而是依赖于SyncTimeProvider的接口ISyncTimeProvider。我们通过Constructor注入实现依赖注入。为了让读者对Unity和PIAB集成的效果具有一个直观的印象,我在SyncTimeProvider上应用了一个CachingCallHandlerAttribute,如果该CallHandler生效,方法执行的结果将会被缓存,在缓存过期之前,得到的时间将是一样的。相应的代码如下所示:



usingSystem;

namespaceArtech.InterceptableUnity

{



publicinterfaceISyncTimeServiceProvision

{

DateTimeGetCurrentTime();

}



publicclassSyncTimeServiceProvisionService:ISyncTimeServiceProvision

{

publicISyncTimeProviderSyncTimeProvider

{get;privateset;}



publicSyncTimeServiceProvisionService([Dependency]ISyncTimeServiceProvidersyncTimeServiceProvider)

{

this.SyncTimeServiceProvider=syncTimeServiceProvider;

}



#regionISyncTimeServiceProvisionMembers



publicDateTimeGetCurrentTime()

{

returnthis.SyncTimeProvider.GetCurrentTime();

}



#endregion

}



publicinterfaceISyncTimeProvider

{

DateTimeGetCurrentTime();

}



[CachingCallHandler]

publicclassSyncTimeProvider:ISyncTimeProvider

{

#regionISyncTimeServiceProviderMembers



publicDateTimeGetCurrentTime()

{

returnDateTime.Now;

}



#endregion

}

}

那么我们就可以通过下面的方式,利用UnityContainer采用基于接口(ISyncTimeServiceProvision)的方式创建SyncTimeServiceProvisionService,并调用GetCurrentTime方法:



usingSystem;

usingSystem.Threading;

usingMicrosoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;

usingMicrosoft.Practices.Unity;

usingMicrosoft.Practices.Unity.InterceptionExtension;

namespaceArtech.InterceptableUnity

{

classProgram

{

staticvoidMain(string[]args)

{

IUnityContainercontainer=newUnityContainer();

container.RegisterType();

container.RegisterType();



container.AddNewExtension();

container.Configure().SetDefaultInterceptorFor(typeof(ISyncTimeServiceProvider),newTransparentProxyInterceptor());

varsyncTimeServiceProvision=container.Resolve();

for(inti=0;i<5;i++)

{

Console.WriteLine(syncTimeServiceProvision.GetCurrentTime());

Thread.Sleep(1000);

}



}

}

}

通过下面的输出,我们看出输出的时间都是相同的,从而证实了CachingCallHandlerAttribute的有效性,进而正式了UnityContainer和PIAB的集成:



image

二、通过自定义UnityContainerExtension的方式实现Unity与PIAB的集成



通过Microsoft.Practices.Unity.InterceptionExtension.Interception对Unity和PIAB两者之间的集成,需要我们借助Interception为每一个需要被劫持(Interception)的类型注册相应的Interceptor(实现接口Microsoft.Practices.Unity.InterceptionExtension.IInterceptor),如下面的代码片断所示。



container.Configure().SetDefaultInterceptorFor(typeof(ISyncTimeServiceProvider),newTransparentProxyInterceptor());

但是在每多情况下,我们不可能预先确定需要注册哪些对象,或者这样的类型很多,手工注册的方式将不具有可操作性。比如,在一个N-Layer的应用中,上层的对象通过UnityContainer创建下层对象,并且通过PIAB的方式将不同的CrosscuttingConcern应用于相应的层次,我们不可能对每一个应用了PAIBCallHandler相关的类型进行Interceptor的注册。



为此,我对Interception进行了扩展,实现了Interceptor的动态注册。Unity采用两种不同的InterceptionStrategy:InstanceInterceptionStrategy和TypeInterceptionStrategy,它们分别采用基于InstanceInterceptor和TypeInterceptor实现方法调用劫持。我继承了InstanceInterceptionStrategy和TypeInterceptionStrategy,将Inteceptor的动态注册定义在PreBuildUp方法中。继承自Interception,在Initialize方法中将两个扩展的InstanceInterceptionStrategy和TypeInterceptionStrategy——ExtendedInstanceInterceptionStrategy和ExtendedTypeInterceptionStrategy添加到UnityContainer的BuildStrategy列表中。在这个扩展的Inteception——ExtendedInterception中,被用于动态注册的Interceptor定义在ExtendedInterception中,默认为TransparentProxyInteceptor。下面是ExtendedInterception、ExtendedInstanceInterceptionStrategy和ExtendedTypeInterceptionStrategy的定义:



ExtendedInterception:



usingMicrosoft.Practices.Unity.InterceptionExtension;

usingMicrosoft.Practices.Unity.ObjectBuilder;

namespaceArtech.InterceptableUnity

{

publicclassExtendedInterception:Interception

{

publicIInterceptorInterceptor

{get;internalset;}



publicExtendedInterception()

{

this.Interceptor=newTransparentProxyInterceptor();

}



protectedoverridevoidInitialize()

{

this.Context.Strategies.Add(newExtendedInstanceInterceptionStrategy(this),UnityBuildStage.Setup);

this.Context.Strategies.Add(newExtendedTypeInterceptionStrategy(this),UnityBuildStage.PreCreation);

this.Context.Container.RegisterInstance(typeof(AttributeDrivenPolicy).AssemblyQualifiedName,newAttributeDrivenPolicy());

}

}

}

ExtendedInstanceInterceptionStrategy:



usingSystem;

usingMicrosoft.Practices.ObjectBuilder2;

usingMicrosoft.Practices.Unity;

usingMicrosoft.Practices.Unity.InterceptionExtension;



namespaceArtech.InterceptableUnity

{

publicclassExtendedInstanceInterceptionStrategy:InstanceInterceptionStrategy

{

publicExtendedInterceptionInterception

{get;privateset;}



publicExtendedInstanceInterceptionStrategy(ExtendedInterceptioninterception)

{

if(null==interception)

{

thrownewArgumentNullException("interception");

}



this.Interception=interception;

}



privatestaticIInstanceInterceptionPolicyFindInterceptorPolicy(IBuilderContextcontext)

{

TypebuildKey=BuildKey.GetType(context.BuildKey);

Typetype=BuildKey.GetType(context.OriginalBuildKey);

IInstanceInterceptionPolicypolicy=context.Policies.Get(context.BuildKey,false)??context.Policies.Get(buildKey,false);

if(policy!=null)

{

returnpolicy;

}

policy=context.Policies.Get(context.OriginalBuildKey,false)??context.Policies.Get(type,false);

returnpolicy;

}



publicoverridevoidPreBuildUp(IBuilderContextcontext)

{

if(BuildKey.GetType(context.BuildKey)==typeof(IUnityContainer))

{

return;

}



IInstanceInterceptionPolicypolicy=FindInterceptorPolicy(context);

if(null!=policy)

{

if(policy.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey)))

{

this.Interception.SetDefaultInterceptorFor(BuildKey.GetType(context.BuildKey),policy.Interceptor);

}

}

else

{

if(this.Interception.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey))&&this.Interception.InterceptorisIInstanceInterceptor)

{

this.Interception.SetDefaultInterceptorFor(BuildKey.GetType(context.BuildKey),(IInstanceInterceptor)this.Interception.Interceptor);

}

}

base.PreBuildUp(context);

}

}

}





ExtendedTypeInterceptionStrategy:



usingSystem;

usingMicrosoft.Practices.ObjectBuilder2;

usingMicrosoft.Practices.Unity.InterceptionExtension;



namespaceArtech.InterceptableUnity

{

publicclassExtendedTypeInterceptionStrategy:TypeInterceptionStrategy

{



publicExtendedInterceptionInterception

{get;privateset;}



publicExtendedTypeInterceptionStrategy(ExtendedInterceptioninterception)

{

if(null==interception)

{

thrownewArgumentNullException("interception");

}



this.Interception=interception;

}



privatestaticITypeInterceptionPolicyGetInterceptionPolicy(IBuilderContextcontext)

{

ITypeInterceptionPolicypolicy=context.Policies.Get(context.BuildKey,false);

if(policy==null)

{

policy=context.Policies.Get(BuildKey.GetType(context.BuildKey),false);

}

returnpolicy;

}



publicoverridevoidPreBuildUp(IBuilderContextcontext)

{

varpolicy=GetInterceptionPolicy(context);

if(null!=policy)

{

if(policy.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey)))

{

this.Interception.SetInterceptorFor(BuildKey.GetType(context.BuildKey),policy.Interceptor);

}

}

else

{

if(this.Interception.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey))&&this.Interception.InterceptorisITypeInterceptor)

{

this.Interception.SetDefaultInterceptorFor(BuildKey.GetType(context.BuildKey),(ITypeInterceptor)this.Interception.Interceptor);

}

}

base.PreBuildUp(context);

}

}

}

那么使用的时候,动态注册Interceptor的操作将不再需要,如下面代码片断所示:



usingSystem;

usingSystem.Threading;

usingMicrosoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;

usingMicrosoft.Practices.Unity;

usingMicrosoft.Practices.Unity.InterceptionExtension;

namespaceArtech.InterceptableUnity

{

classProgram

{

staticvoidMain(string[]args)

{

IUnityContainercontainer=newUnityContainer();

container.RegisterType();

container.RegisterType();



ExtendedInterceptioninterception=newExtendedInterception();

interception.Interceptor=newTransparentProxyInterceptor();

container.AddExtensionwww.wang027.com(interception);

varsyncTimeServiceProvision=container.Resolve();

for(inti=0;i<5;i++)

{

Console.WriteLine(syncTimeServiceProvision.GetCurrentTime());

Thread.Sleep(1000);

}

}

}





三、通过配置的方式应用ExtendedInterception

为了通过配置的方式应用ExtendedInterception,我们需要为之定义相应的配置类型,一个继承自Microsoft.Practices.Unity.Configuration.UnityContainerExtensionConfigurationElement得类型。为此,我定义了下面一个ExtendedInterceptionElement类型,配置属性为默认的Inteceptor的类型。



usingSystem;

usingSystem.Configuration;

usingMicrosoft.Practices.Unity;

usingMicrosoft.Practices.Unity.Configuration;

usingMicrosoft.Practices.Unity.InterceptionExtension;

namespaceArtech.InterceptableUnity

{

publicclassExtendedInterceptionElement:UnityContainerExtensionConfigurationElement

{

[ConfigurationProperty("interceptor",IsRequired=false,DefaultValue="")]

publicstringInterceptor

{

get

{

return(string)this["interceptor"];

}

}



publicoverridevoidConfigure(IUnityContainercontainer)

{

base.Configure(container);

ExtendedInterceptioninterception=newExtendedInterception();

if(!string.IsNullOrEmpty(this.Interceptor))

{

vartype=System.Type.GetType(this.Interceptor);

if(null==type)

{

thrownewConfigurationErrorsException(string.Format("The{0}isnotavalidInterceptor.",this.Interceptor));

}



if(!typeof(IInterceptor).IsAssignableFrom(type))

{

thrownewConfigurationErrorsException(string.Format("The{0}isnotavalidInterceptor.",this.Interceptor));

}

interception.Interceptor=(IInterceptor)Activator.CreateInstance(type);

}



container.AddExtension(interception);

}

}

}

那么对于上面的例子,我么可以将TypeMapping和ExtendedInterception扩展定义在如下一个配置文件中:










Culture=neutral,PublicKeyToken=31bf3856ad364e35"/>

















































那么我们的代码将会变得异常简洁:



usingSystem;

usingSystem.Threading;

usingMicrosoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;

usingMicrosoft.Practices.Unity;

usingMicrosoft.Practices.Unity.InterceptionExtension;

usingMicrosoft.Practices.Unity.Configuration;

usingSystem.Configuration;

namespaceArtech.InterceptableUnity

{

classProgram

{

staticvoidMain(string[]args)

{

IUnityContainercontainer=newUnityContainer();

UnityConfigurationSectionconfiguration=(UnityConfigurationSection)ConfigurationManager.GetSection("unity");

configuration.Containers.Default.Configure(container);

varsyncTimeServiceProvision=container.Resolve();

for(inti=0;i<5;i++)

{

Console.WriteLine(syncTimeServiceProvision.GetCurrentTime());

Thread.Sleep(1000);

}

}

}

}

献花(0)
+1
(本文系thedust79首藏)