[IoC容器Unity]第四回:使用范例


1.引言 

前面幾個章節介紹了Unity的基本使用,主要分為程序和配置文件兩種方法的使用,可以參考一下鏈接,

本節作為結束篇,將介紹一下在項目中如何應用Unity。   

2.范例

Unity應用廣泛,在很多開源項目中都可以找到Unity的身影。就拿微軟的開源項目新聞發布系統 Kigg 舉例,Kigg的依賴注入就是使用到了Unity,大家可以下載。Kigg是MVC應用的一個推薦范例,本節介紹一下其中的依賴注入IoC容器,該容器在Kigg.Core項目,Infrastructure目錄下的IOC目錄,該目錄下有4個類,如下圖

先看看 IDependencyResolver 接口聲明

IDependencyResolver 聲明
    public interface IDependencyResolver : IDisposable
    {
        /// <summary>
        /// 注冊 T類型實例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        void Register<T>(T instance);

        /// <summary>
        /// 注入 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="existing"></param>
        void Inject<T>(T existing);

        /// <summary>
        /// 解析
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        T Resolve<T>(Type type);

        T Resolve<T>(Type type, string name);

        T Resolve<T>();

        T Resolve<T>(string name);

        IEnumerable<T> ResolveAll<T>();
    }

看到該接口定義,我們很快會想到Unity中的IUnityContainer容器接口,對的,里面的方法和作用 跟IUnityContainer接口類似。
那為什么不直接使用IUnityContainer而要定義一個類似的接口IDependencyResolver呢? 
可以看到Kigg的IDependencyResolver是定義在核心層Kigg.Core相當於基礎架構層中,而這個層是一個核心庫,其它層都會引用它,Kigg應用了一種像 適配器模式來進行封裝。
就是系統中要應用其它的外部接口,比如Unity 和Sping.net的依賴注入的方法不統一,我們要進行封裝,可以先定義一個公共接口,再利用Unity和其它組件來實現它,這就是IDependencyResolver的由來。

 

Kigg中已經利用Unity來實現IDependencyResolver接口,當然我們也可以用其他的依賴注入容器來實現它,下面看看UnityDependencyResolver的實現

UnityDependencyResolver 聲明
public class UnityDependencyResolver : DisposableResource, IDependencyResolver
    {
        //注入容器
        private readonly IUnityContainer _container;

        [DebuggerStepThrough]
        public UnityDependencyResolver() : this(new UnityContainer())
        {
            UnityConfigurationSection configuration = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
            configuration.Containers.Default.Configure(_container);
        }

        [DebuggerStepThrough]
        public UnityDependencyResolver(IUnityContainer container)
        {
            Check.Argument.IsNotNull(container, "container");

            _container = container;
        }

        [DebuggerStepThrough]
        public void Register<T>(T instance)
        {
            Check.Argument.IsNotNull(instance, "instance");
            //注冊實例
            _container.RegisterInstance(instance);
        }

        [DebuggerStepThrough]
        public void Inject<T>(T existing)
        {
            Check.Argument.IsNotNull(existing, "existing");
            //注入加載
            _container.BuildUp(existing);
        }

        [DebuggerStepThrough]
        public T Resolve<T>(Type type)
        {
            Check.Argument.IsNotNull(type, "type");
            //解析
            return (T) _container.Resolve(type);
        }

        [DebuggerStepThrough]
        public T Resolve<T>(Type type, string name)
        {
            Check.Argument.IsNotNull(type, "type");
            Check.Argument.IsNotEmpty(name, "name");

            return (T) _container.Resolve(type, name);
        }

        [DebuggerStepThrough]
        public T Resolve<T>()
        {
            return _container.Resolve<T>();
        }

        [DebuggerStepThrough]
        public T Resolve<T>(string name)
        {
            Check.Argument.IsNotEmpty(name, "name");

            return _container.Resolve<T>(name);
        }

        [DebuggerStepThrough]
        public IEnumerable<T> ResolveAll<T>()
        {
            //解析容器中所有
            IEnumerable<T> namedInstances = _container.ResolveAll<T>();
            T unnamedInstance = default(T);

            try
            {
                unnamedInstance = _container.Resolve<T>();
            }
            catch (ResolutionFailedException)
            {
                //When default instance is missing
            }

            if (Equals(unnamedInstance, default(T)))
            {
                return namedInstances;
            }

            return new ReadOnlyCollection<T>(new List<T>(namedInstances) { unnamedInstance });
        }

        [DebuggerStepThrough]
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _container.Dispose();
            }

            base.Dispose(disposing);
        }
    }

可以看到UnityDependencyResolver的默認構造函數是加載配置文件(配置文件在Web.Config中)來初始化IUnityContainer,你也可以用編程的方式。
實現方式中沒有繼承IUnityContainer或者UnityContainer,而是和IUnityContainer是組合關系,這樣更加的靈活,這是對象的Adapter模式,就是組合模式。如果有其它的IoC容器,如Windsor/StructureMap/Spring.Net等等,可以實現IDependencyResolver接口即可。

 

使用時,只需要實例化對應的IDependencyResolver對象就可以了,Kigg中為了更好的控制IDependencyResolver對象的創建,利用了工廠方法來創建。
先看看工廠接口IDependencyResolverFactory

IDependencyResolverFactory定義
    public interface IDependencyResolverFactory
    {
        /// <summary>
        /// 創建IDependencyResolver的實例
        /// </summary>
        /// <returns></returns>
        IDependencyResolver CreateInstance();
    }

看到定義,只有一個方法CreateInstance,就是用來創建IDependencyResolver對象,我們可以實現該工廠,可以直接new UnityDependencyResolver來創建,Kigg中利用配置文件方式,看DependencyResolverFactory的聲明如下:

DependencyResolverFactory 定義
    public class DependencyResolverFactory : IDependencyResolverFactory
    {
        private readonly Type _resolverType;

        public DependencyResolverFactory(string resolverTypeName)
        {
            Check.Argument.IsNotEmpty(resolverTypeName, "resolverTypeName");
            //GetType(名字,是否區分大小,是否異常)
            _resolverType = Type.GetType(resolverTypeName, true, true);
        }

        public DependencyResolverFactory() : this(new ConfigurationManagerWrapper().AppSettings["dependencyResolverTypeName"])
        {
        }

        public IDependencyResolver CreateInstance()
        {
            //根據類型創建實例對象
            return Activator.CreateInstance(_resolverType) as IDependencyResolver;
        }
    }

可以看到默認構造函數是讀取配置文件dependencyResolverTypeName節點,利用反射Activator.CreateInstance進行創建,看看dependencyResolverTypeName節點定義,在Kigg.Web項目的配置文件中,如下:

    <appSettings>
        <clear/>
        <add key="dependencyResolverTypeName" value="Kigg.Infrastructure.EnterpriseLibrary.UnityDependencyResolver, Kigg.Infrastructure.EnterpriseLibrary"/>
    </appSettings>

還有其它IoC容器實現時,只要更改配置文件就行。

 

使用時可以調用工廠方法進行創建IDependencyResolver對象,每次使用時都得利用工廠來創建,IDependencyResolver里面的方法肯定都是實例方法,使用也不方便,Kigg為我們進行封裝成靜態方法,看IoC類的聲明

IoC 定義
 public static class IoC
    {
        //解析器
        private static IDependencyResolver _resolver;

        /// <summary>
        /// 初始化,創建實例對象
        /// </summary>
        /// <param name="factory"></param>
        [DebuggerStepThrough]
        public static void InitializeWith(IDependencyResolverFactory factory)
        {
            Check.Argument.IsNotNull(factory, "factory");

            _resolver = factory.CreateInstance();
        }

        /// <summary>
        /// 注冊對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        [DebuggerStepThrough]
        public static void Register<T>(T instance)
        {
            Check.Argument.IsNotNull(instance, "instance");

            _resolver.Register(instance);
        }

        /// <summary>
        /// 注入對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="existing"></param>
        [DebuggerStepThrough]
        public static void Inject<T>(T existing)
        {
            Check.Argument.IsNotNull(existing, "existing");

            _resolver.Inject(existing);
        }

        /// <summary>
        /// 解析對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static T Resolve<T>(Type type)
        {
            Check.Argument.IsNotNull(type, "type");

            return _resolver.Resolve<T>(type);
        }
        /// <summary>
        /// 解析對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static T Resolve<T>(Type type, string name)
        {
            Check.Argument.IsNotNull(type, "type");
            Check.Argument.IsNotEmpty(name, "name");

            return _resolver.Resolve<T>(type, name);
        }
        /// <summary>
        /// 解析對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static T Resolve<T>()
        {
            return _resolver.Resolve<T>();
        }
        /// <summary>
        /// 解析對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static T Resolve<T>(string name)
        {
            Check.Argument.IsNotEmpty(name, "name");

            return _resolver.Resolve<T>(name);
        }
        /// <summary>
        /// 解析對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static IEnumerable<T> ResolveAll<T>()
        {
            return _resolver.ResolveAll<T>();
        }
        /// <summary>
        /// 銷毀
        /// </summary>
        [DebuggerStepThrough]
        public static void Reset()
        {
            if (_resolver != null)
            {
                _resolver.Dispose();
            }
        }
    }

IDependencyResolver是IoC的一個私有靜態成員,私有的,那怎么創建對象,IoC類有一個InitializeWith(IDependencyResolverFactory factory),利用工廠方法來創建,以后我們只要使用IoC這個類就行。IoC靜態類並沒有在靜態構造函數中初始化IDependencyResolver,考慮到依賴,只依賴比較穩定的接口,而不會依賴具體的實現如DependencyResolverFactory,這樣就可以提供方法供外面訪問來進行創建IDependencyResolver對象。

那IDependencyResolver什么時候會創建,由於沒有在構造函數中實現創建,必定要調用IoC的InitializeWith方法,我們可以找到引用,看到一個啟動引導Bootstrapper類如下:

Bootstrapper定義
    public static class Bootstrapper
    {
        static Bootstrapper()
        {
            try
            {
                IoC.InitializeWith(new DependencyResolverFactory()); 
            }
            catch (ArgumentException)
            {
                // Config file is Missing
            }
        }

        public static void Run()
        {
            IoC.ResolveAll<IBootstrapperTask>().ForEach(t => t.Execute());
        }
    }

在Bootstrapper的構造函數中進行了IDependencyResolver的創建,即在第一次使用Bootstrapper時會創建,那肯定的是Bootstrapper一定要在IoC之前使用啊,不然在使用IoC類時肯定報錯,不用擔心,Bootstrapper使用的很早,因為它是一個引導啟動類,查找引用,可以看到在Kigg.Web下的Global.asax文件中找到,聲明如下

GlobalApplication 定義
    public class GlobalApplication : HttpApplication
    {

        public static void OnStart()
        {
            Bootstrapper.Run();
            Log.Info("Application Started");
        }

        public static void OnEnd()
        {
            Log.Warning("Application Ended");
            IoC.Reset();
        }

        protected void Application_Start()
        {
            OnStart();
        }

        protected void Application_End()
        {
            OnEnd();
        }
    }

原來在Application_Start中,程序啟動時就使用到了,好了,以后就直接使用Ioc來創建依賴對象吧,不用new了,Unity的配置文件都是在Web.Config中,就不介紹了,Kigg的依賴容器就介紹完畢了。

3.小結

我們使用時如果想要IoC容器容易擴展容易使用可以參照Kigg。

IUnityContainer容器可以聲明N個對象,那樣就不好管理了,我們應該只要一個,即創建一次,可以使用static靜態成員。

不用擔心一個IUnityContainer容器中注冊了太多對象關系而影響解析性能,IUnityContainer中是維護着許多字典,就是說注冊100個跟注冊100W個映射是一樣的。

IUnityContainer可以通過編程映射和配置文件映射,推薦配置文件映射。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM