Unity Ioc 依賴倒置及Untity AOP被動攔截/自動攔截

Unity Ioc 介紹:html

Unity是微軟團隊開發的一個輕量級,可擴展的依賴注入容器,爲鬆散耦合應用程序提供了很好的解決方案,支持構造器注入,屬性注入,方法注入。同時由於把對象交給容器建立,有沒有可能在建立的時候作些手腳和功能呢?答案是確定的。app

目前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容器不存在一個到該對象的引用。

看一個例子:
ide

 

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均可以支持單例模式。

修改上面的代碼爲:
post

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,即支持單例模式。

看個例子:

this

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類的同一實例,指向同一內存地址。

spa

 

Unity是微軟P&P部門開發的一個輕量級IoC框架,經過Interception機制能夠實現基於三種攔截機制的AOP。不過Unity僅僅提供「顯式」攔截機制,以至咱們爲了註冊可被攔截的類型會多寫不少代碼和配置。本篇文章經過UnityContainer的擴展提供了一種「自動」攔截機制。設計

1、顯式攔截

咱們經過一個簡單的實例演示Unity原生支持的顯式攔截機制和咱們經過擴展實現的自動攔截機制。咱們定了以下一個簡單的SimpleCallHandler,在Invoke方法中經過在控制檯打印一段文字用以證實應用在某個類型上的CallHandler被執行了。code

   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方法上。orm

   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...

2、自動攔截

若是經過咱們自定義的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();

3、應用不一樣的攔截機制

在默認的狀況下,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...

4、支持配置

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

下載源代碼。

 

出處:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html

相關文章
相關標籤/搜索