• [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可以通过编程映射和配置文件映射,推荐配置文件映射。

  • 相关阅读:
    FMXUI TEXTVIEW代码设置IMAGEINDEX
    WIN10无法识别安卓设备,提示Windows 无法验证此设备所需的驱动程序的数字签名
    FMX Android ZIP解压中文乱码
    Fmx在android下InputBox输入框点击Cancel取消按钮后报异常
    BLUEHOST香港主机FTP连接不上解决办法
    Delphi abstract error异常
    DELPHI FMX IOS模拟器调试时出现No SDKs could be found
    VM安装OSX进度条一半时卡住不动,【附】OSX10.10 ISO镜像文件
    Tengine 安装配置全过程(nginx 同理)
    php 运行的四种模式
  • 原文地址:https://www.cnblogs.com/qixuejia/p/7836285.html
Copyright © 2020-2023  润新知