通过定义UnityContainer扩展变”Explicit Interception”为”Automatic Interception”
Unity是微软P&P部门开发的一个轻量级IoC框架,通过Interception机制可以实现基于三种拦截机制的AOP。不过Unity仅仅提供“显式”拦截机制,以致我们为了注册可被拦截的类型会多写很多代码和配置。本篇文章通过UnityContainer的扩展提供了一种“自动”拦截机制。
一、显式拦截
我们通过一个简单的实例演示Unity原生支持的显式拦截机制和我们通过扩展实现的自动拦截机制。我们定了如下一个简单的SimpleCallHandler,在Invoke方法中通过在控制台打印一段文字用以证明应用在某个类型上的CallHandler被执行了。
1: public class SimpleCallHandler : ICallHandler
2: {
3: public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
4: {
5: Console.WriteLine("The CallHandler applied to \"{0}\" is invoked.", input.Target.GetType().Name);
6: return getNext()(input, getNext);
7: }
8: public int Order { get; set; }
9: }
10: public class SimpleCallHandlerAttribute : HandlerAttribute
11: {
12: public override ICallHandler CreateHandler(IUnityContainer container)
13: {
14: return new SimpleCallHandler { Order = this.Order };
15: }
16: }
然后我们创建了如下所示的一个接口IFoo和三个类Foo、Bar和Baz。其中Foo实现了接口IFoo,而Foo依赖于Bar,Bar依赖于Baz。我们以构造器注入的方式定义Foo和Bar。SimpleCallHandler被同时应用到了Foo、Bar和Baz的DoSth方法上。
1: public interface IFoo
2: {
3: void DoSth();
4: }
5:
6: public class Foo : IFoo
7: {
8: public Bar Bar { get; private set; }
9: public Foo(Bar bar)
10: {
11: this.Bar = bar;
12: }
13: [SimpleCallHandler]
14: public virtual void DoSth()
15: {
16: this.Bar.DoSth();
17: }
18: }
19: public class Bar : MarshalByRefObject
20: {
21: public Baz Baz { get; private set; }
22: public Bar(Baz baz)
23: {
24: this.Baz = baz;
25: }
26: [SimpleCallHandler]
27: public virtual void DoSth()
28: {
29: this.Baz.DoSth();
30: }
31: }
32: public class Baz : MarshalByRefObject
33: {
34: [SimpleCallHandler]
35: public void DoSth()
36: {
37: Console.WriteLine("Done...");
38: }
39: }
所谓显式拦截就是说:如果某个类型需要被拦截处理,比如将其显式地注册为“可被拦截的类型”,并且需要显式地注册拦截器(决定拦截机制)和拦截行为。对于本实例来说,为了上应用在Foo、Bar和Baz上的CallHandler能够起作用,我们需要通过如下的方式对这三个类型进行显式地拦截注册。
1: IUnityContainer container = new UnityContainer();
2: container.AddNewExtension<Interception>()
3: .RegisterType<IFoo, Foo>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
4: .RegisterType<Bar>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
5: .RegisterType<Baz>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>());
6:
7: IFoo foo = container.Resolve<IFoo>();
8: foo.DoSth();
运行结果:
1: The CallHandler applied to "Foo" is invoked.
2: The CallHandler applied to "Bar" is invoked.
3: The CallHandler applied to "Baz" is invoked.
4: Done...
二、自动拦截
如果通过我们自定义的UnityContainer扩展AutoInterception,你就无须对需要被拦截的类型进行显式注册。而相关的代码将会变得简单,运行如下一段代码,你依然会得到同上面一样的结果。
1: IUnityContainer container = new UnityContainer();
2: container.AddNewExtension<AutoInterception>()
3: .AddNewExtension<Interception>()
4: .RegisterType<IFoo, Foo>();
5:
6: IFoo foo = container.Resolve<IFoo>();
7: foo.DoSth();
三、应用不同的拦截机制
在默认的情况下,AutoInterception采用的拦截器为TransparentProxyInterceptor。我们通过通过配置AutoInterception的方式来应用其它两种拦截器,即InterfaceInterceptor和VirtualMethodInterceptor。由于在下面的代码中采用了InterfaceInterceptor,所有只有实现了IFoo接口的Foo对象才会被拦截。
1: IUnityContainer container = new UnityContainer();
2: container.AddNewExtension<AutoInterception>()
3: .AddNewExtension<Interception>()
4: .Configure < AutoInterception>().RegisterInterceptor(new InterfaceInterceptor())
5: .RegisterType<IFoo, Foo>();
6:
7: IFoo foo = container.Resolve<IFoo>();
8: foo.DoSth();
执行结果:
1: The CallHandler applied to "Foo" is invoked.
2: Done...
如果我们采用VirtualMethodInterceptor的话,只有定义在需方法的Foo和Bar的DoSth方法才会被拦截。
1: IUnityContainer container = new UnityContainer();
2: container.AddNewExtension<AutoInterception>()
3: .AddNewExtension<Interception>()
4: .Configure < AutoInterception>().RegisterInterceptor(new VirtualMethodInterceptor())
5: .RegisterType<IFoo, Foo>();
6:
7: IFoo foo = container.Resolve<IFoo>();
8: foo.DoSth();
输出结果:
1: The CallHandler applied to "Wrapped_Foo_6c22528df1b64d3886e9955cd8961ca7" is invoked.
2: The CallHandler applied to "Wrapped_Bar_c10e3640a27d469c8872ec4193303897" is invoked.
3: Done...
四、支持配置
AutoInterception不仅仅支持Unity提供的Policy Injection配置,还可以通过配置指定采用的拦截器类型。现在我们将应用在Foo、Bar和Baz上的SimpleCallHandlerAttribute特性全部删除,通过如下的配置将该CallHandler应用到所有的DoSth方法上。这个配置还指定了采用的拦截器类型为VirtualMethodInterceptor。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="unity"
5: type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
6: </configSections>
7: <unity>
8: <alias alias="SimpleCallHandler" type="Artech.UnityExtensions.SimpleCallHandler, Artech.UnityExtensions" />
9: <alias alias="IFoo" type="Artech.UnityExtensions.IFoo, Artech.UnityExtensions" />
10: <alias alias="Foo" type="Artech.UnityExtensions.Foo, Artech.UnityExtensions" />
11: <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
12: <sectionExtension type="Artech.UnityExtensions.Configuration.AutoInterceptionConfigurationExtension, Artech.UnityExtensions" />
13: <container>
14: <extension type="AutoInterception"/>
15: <extension type="Interception"/>
16: <register type="IFoo" mapTo="Foo"/>
17: <interception>
18: <policy name="service">
19: <matchingRule name="MemberNameMatchingRule" type="MemberNameMatchingRule">
20: <constructor>
21: <param name="nameToMatch" value="DoSth"/>
22: </constructor>
23: </matchingRule>
24: <callHandler name="SimpleCallHandler" type="SimpleCallHandler"/>
25: </policy>
26: </interception>
27: <autoInterception>
28: <interceptor type="VirtualMethodInterceptor"/>
29: </autoInterception>
30: </container>
31: </unity>
32: </configuration>
我们通过如下的代码,通过加载配置的方式来配置创建的UnityContainer。最终直接通过解析接口IFoo得到Foo对象,并调用其DoSth方法。
1: IUnityContainer container = new UnityContainer();
2: UnityConfigurationSection unitySettings = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
3: unitySettings.Configure(container);
4: IFoo foo = container.Resolve<IFoo>();
5: foo.DoSth();
由于我们采用的是VirtualMethodInterceptor,所有只有Foo和Bar中定义的需方法才能被拦截,这可以通过如下的输出结果得到证实:
1: The CallHandler applied to "Wrapped_Foo_53c9f355fbac4acdaf405b2a92d0bd7a" is invoked.
2: The CallHandler applied to "Wrapped_Bar_8cdbf768e96c434da36ed1f181c2d6cd" is invoked.
3: Done...
虽然AutoInterception实现的逻辑并不复杂,但是对于不了解Unity设计的人来说也不是那么容易理解的。所以我并不打算介绍其内部原理,又兴趣的读者可以从这里下载源代码。