• Unity Ioc 学习笔记1


    Unity Ioc 介绍:

    Unity是微软团队开发的一个轻量级,可扩展的依赖注入容器,为松散耦合应用程序提供了很好的解决方案,支持构造器注入,属性注入,方法注入。同时因为把对象交给容器创建,有没有可能在创建的时候做些手脚和功能呢?答案是肯定的。

    目前Unity中提供两个Lifetime Manager类可供我们直接使用,当然你也可以实现自己的Lifetime Manager类。

    1. ContainerControlledLifetimeManager

    Unity保存一个指向对象实例的引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例。也就是说实现了对象单例模式。默认情况下,RegisterInstance方法使用该Lifetime Manager。

    2. ExternallyControlledLifetimeManager

    Unity仅保存一个指向对象实例的弱引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例。但是由于当对象创建完之后,容器没有对该对象的强引用,所以就可能出现当其他地方没有去强引用它时候,会被GC回收掉。

    先看看一个接口和类,下面会用到

    public interface IPlayer
    {
        
    void Play();
    }

    public class Mp3Player : IPlayer
    {
        
    public void Play()
        {
            Console.WriteLine(
    "Playing Mp3");
        }
    }


    接下来通过在RegisterType和RegisterInstance时指定相应的Lifetime Manager来介绍Lifetime Manager的应用场景。

    1. RegisterType

    当用RegisterType注册映射关系时,如果没有指定LifetimeManager,默认是使用一个瞬态的Lifetime Manager。即每次通过Unity容器获取对象的实例时都会重新创建一个该实例,也就是说Unity容器不存在一个到该对象的引用。

    看一个例子:

     

    IUnityContainer container = new UnityContainer();

    container.RegisterType
    <IPlayer, Mp3Player>();

    IPlayer player1 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player1 HashCode: {0}",player1.GetHashCode()));

    IPlayer player2 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player2 HashCode: {0}",player2.GetHashCode()));

    输出结果:

    Unity 7-1.jpg

    通过输出的player1和player2对象的HashCode值可以看出,player1和player2分别是Mp3Player类的不同实例。

    那怎样实现单例模式呢?

    要实现单例模式,容器需要保存一个指向对象实例的引用。通过在RegisterType时为它指定相应的Lifetime Manager可以实现单例模式,从上面对ContainerControlledLifetimeManager和ExternallyControlledLifetimeManager的介绍可以知道,这两个Lifetime Manager都可以支持单例模式。

    修改上面的代码为:

    IUnityContainer container = new UnityContainer();

    //这里指定使用ContainerControlledLifetimeManager对象
    container.RegisterType<IPlayer, Mp3Player>(new ContainerControlledLifetimeManager());

    IPlayer player1 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player1 HashCode: {0}",player1.GetHashCode()));

    IPlayer player2 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player2 HashCode: {0}",player2.GetHashCode()));


    看看输出:

    Unity 7-2.jpg

    通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。

    2. RegisterInstance

    当用RegisterInstance注册映射关系时,如果没有指定Lifetime Manager,默认是使用ContainerControlledLifetimeManager,即支持单例模式。

    看个例子:

    IUnityContainer container = new UnityContainer();

    IPlayer mp3Player 
    = new Mp3Player();
    container.RegisterInstance
    <IPlayer>(mp3Player);

    IPlayer player1 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player1 HashCode: {0}", player1.GetHashCode()));

    IPlayer player2 
    = container.Resolve<IPlayer>();
    Console.WriteLine(
    string.Format("Player2 HashCode: {0}", player2.GetHashCode()));



    看看输出:

    Unity 7-3.jpg

    通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。

    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设计的人来说也不是那么容易理解的。所以我并不打算介绍其内部原理,又兴趣的读者可以从

    /Files/scottpei/AutoInterception.rar

    下载源代码。

  • 相关阅读:
    ThinkDev组件库 开篇
    使用 CodeIgniter 框架快速开发 PHP 应用(六)
    使用 CodeIgniter 框架快速开发 PHP 应用(五)
    jQuery EasyUI 的截图插件(imgAreaSelect)用法
    细说UI线程和Windows消息队列
    ASP.NET MVC2 异常处理机制中令人费解的HTTP 500错误
    从了解到深入——剖析WF4的数据流
    .NET 4.0中数组的新增功能
    成功就是把自己的特长发挥得淋漓尽致(更新)
    WPF4数据绑定应用之“创建具有多种显示效果的字串”
  • 原文地址:https://www.cnblogs.com/scottpei/p/2851087.html
Copyright © 2020-2023  润新知