zoukankan      html  css  js  c++  java
  • 依赖注入

    依赖注入

    [IoC容器Unity]第三回:依赖注入

    2013-02-22 10:43 by pengdajian, 751 阅读, 2 评论, 收藏编辑

    1.引言

    上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入、属性注入、方法注入,所谓注入相当赋值,下面一个一个来介绍。

    2.构造函数注入

    Unity利用Resolve方法解析一个对象,都是调用注册类型的构造函数来初始化的,初始化时,Unity能够控制初始化的值,当然,我们要给Unity提供足够的原料,要不然也是巧妇难无米之炊,下面看一些简单的示例。

    先准备几个类如下:

    复制代码
    复制代码
        /// <summary>
        /// 班级接口
        /// </summary>
        public interface IClass
        {
            string ClassName { get; set; }
    
            void ShowInfo();
        }
        /// <summary>
        /// 计科班
        /// </summary>
        public class CbClass : IClass
        {
            public string ClassName { get; set; }
    
            public void ShowInfo()
            {
                Console.WriteLine("计科班:{0}", ClassName);
            }
        }
        /// <summary>
        /// 电商班
        /// </summary>
        public class EcClass : IClass
        {
            public string ClassName { get; set; }
    
            public void ShowInfo()
            {
                Console.WriteLine("电商班:{0}", ClassName);
            }
        }
    
        /// <summary>
        /// 学生接口
        /// </summary>
        public interface IStudent
        {
            string Name { get; set; }
            //就读班级
            void ShowInfo();
        }
        /// <summary>
        /// 学生
        /// </summary>
        public class QlinStudent : IStudent
        {
            public string Name { get; set; }
    
            private IClass ToClass { get; set; }
    
            public QlinStudent(IClass _class)
            {
                ToClass = _class;
            }
    
            public void ShowInfo()
            {
                Console.WriteLine("{0}就读班级:{1}", Name, ToClass.ClassName);
            }
        }
    复制代码
    复制代码

    是一个班级和学生的结构,现在我们要解析一个学生IStudent,我们看到具体学生类QlinStudent的构造函数需要一个班级接口,当然要 给IUnityContainer容器提供这个班级映射还有学生自己的映射,就你要什么东东,首先要提供IUnityContainer什么东东。

    2.1 默认方式

    默认方式跟new一个对象,它会根据你提供的材料,选择一个构造函数,即要有构造器要能访问权限,用Public修饰,构造函数的参数也要提供,即IClass也要能解析,不然就报错了,编程注入方式如下:

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
                //默认注册(无命名),如果后面还有默认注册会覆盖前面的
                container.RegisterType<IClass, CbClass>();
                container.RegisterType<IStudent, QlinStudent>();
                //解析默认对象
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();
            }
    复制代码
    复制代码

    配置文件方式 如下:

    复制代码
    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
      </configSections>
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <!--引用命名空间-->
        <namespace name="ConsoleApplication1.UnityDemo.Constructor" />
        <!--引用程序集-->
        <assembly name="ConsoleApplication1" />
        <!--容器-->
        <container name="FirstClass">
          <!--映射关系-->
          <register type="IClass"  mapTo="CbClass"></register>
          <register type="IClass" name="ec" mapTo="EcClass"></register>
          <register type="IStudent"  mapTo="QlinStudent">
    
          </register>
        </container>
      </unity>
    </configuration>
    复制代码
    复制代码

     以下是加载配置文件

    复制代码
    复制代码
            public static void ConStructorConfigTest1()
            {
                IUnityContainer container = new UnityContainer();
                string configFile = "http://www.cnblogs.com/UnityDemo/Constructor/Unity.config";
                var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile };
                //从config文件中读取配置信息
                Configuration configuration =
                    ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
                //获取指定名称的配置节
                UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection("unity");
    
                //载入名称为FirstClass 的container节点
                container.LoadConfiguration(section, "FirstClass");
    
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();
            }
    复制代码
    复制代码

    2.2 指定构造函数

    如果构造函数有多个,它也会按照上面那样来初始化一个对象,我们还可以显示用InjectionConstructor特性来指定一个构造函数来解析对象,如下声明:

    复制代码
    复制代码
        public class QlinStudent : IStudent
        {
            private string Name { get; set; }
    
            private IClass ToClass { get; set; }
    
    
            public QlinStudent()
            {
            }
    
            [InjectionConstructor]
            public QlinStudent(IClass _class,string name)
            {
                ToClass = _class;
                Name = name;
            }
    
            public void ShowInfo()
            {
                Console.WriteLine("{0}就读班级:{1}", Name, ToClass.ClassName);
            }
        }
    复制代码
    复制代码

    2.3 指定参数依赖的注册名称

    构造函数中IClass参数,如果IUnityContainer注册了多个,默认是使用无名称的那个注册,也可以通过Dependency依赖哪个名称来指定哪个来注册,代码,指定ec名称如下:

            [InjectionConstructor]
            public QlinStudent([Dependency("ec")]IClass _class)
            {
                ToClass = _class;
            }

    下面注册一个名称为ec的映射,如果没有名称ec的映射将报错

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
    
                //默认注册(无命名),如果后面还有默认注册会覆盖前面的
                container.RegisterType<IClass, CbClass>();
                //命名注册
                container.RegisterType<IClass, EcClass>("ec");
                container.RegisterType<IStudent, QlinStudent>();
    
                //解析默认对象
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();
            }
    复制代码
    复制代码

    配置文件方式,代码不变,配置中添加一个 name属性就行,如下:

    复制代码
    复制代码
        <container name="FirstClass">
          <!--映射关系-->
          <register type="IClass"  mapTo="CbClass"></register>
          <register type="IClass" name="ec" mapTo="EcClass"></register>
          <register type="IStudent"  mapTo="QlinStudent">
          </register>
        </container>
    复制代码
    复制代码

    2.4 指定参数值

    构造器中的参数也可以依赖一个指定的类型值,如下代码依赖于EcClass类型,可以让构造函数中可以传入一个具体的类型,这也是构造函数传参数,如下:

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
    
                //默认注册(无命名),如果后面还有默认注册会覆盖前面的
                container.RegisterType<IClass, CbClass>();
                //命名注册
                container.RegisterType<IClass, EcClass>("ec");
                container.RegisterType<IStudent, QlinStudent>(new InjectionConstructor(new CbClass()));
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();
            }
    复制代码
    复制代码

    或者注册一个实例对象,如下:

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
                IClass cbClass = new CbClass { ClassName="计科051班" };
                //实例注册命名实例
                container.RegisterInstance<IClass>("ec", cbClass);
                container.RegisterType<IStudent, QlinStudent>();
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();
            }
    复制代码
    复制代码

    配置文件也可以指定类型依赖,如下,指定EcClass:

    复制代码
    复制代码
          <register type="IStudent"  mapTo="QlinStudent">
            <constructor>
              <param name="_class" type="IClass">
                <dependency  type="EcClass"/>
              </param>
            </constructor>
          </register>
    复制代码
    复制代码

    上面已经介绍了传参数,是用InjectionConstructor类型,现在构造函数,多一个参数,如下:

            [InjectionConstructor]
            public QlinStudent([Dependency("ec")]IClass _class, string name)
            {
                ToClass = _class;
                Name = name;
            }

    多了一个name参数,那必须为容器IUnityContainer提供这个参数,没有这个原材料,它无法构造,就会报错,如下代码:

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
    
                container.RegisterType<IStudent, QlinStudent>(new InjectionConstructor(new CbClass() { ClassName = "计科051" }, "Qlin"));
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();           
            }
    复制代码
    复制代码

    注入参数后,也可以下次解析的时候,通过ParameterOverrides类来覆盖原来的参数,改变参数值,如下:

    复制代码
    复制代码
            public static void ConStructorCodeTest1()
            {
                IUnityContainer container = new UnityContainer();
                container.RegisterType<IStudent, QlinStudent>(new InjectionConstructor(new CbClass() { ClassName = "计科051" }, "Qlin"));
                IStudent student = container.Resolve<IStudent>();
                student.ShowInfo();
    
                //覆盖参数解析
                IStudent student1 = container.Resolve<IStudent>(new ParameterOverrides()
                                                                   {
                                                                      {"_class",new EcClass(){ ClassName="电商051"}},
                                                                      {"name","linq"}
                                                                   });
                student1.ShowInfo();
            }
    复制代码
    复制代码

    3.属性注入

    就是Unity容器解析对象时,为属性赋值,有操作权限要Public修饰属性。属性注入方式和构造函数注入类似,只需在需要注入的属性上增加一个 Dependency特性,Dependency指定一个注册名称name参数用来指定注入对象的名称,属性注入也是伴随着类型初始化时注入的,在解析时 自动注入,所以解析时跟以前一样。代码修改如下,在ToClass属性上增加了Dependency特性,来表示这个属性需要注入:

    复制代码
    复制代码
        public class QlinStudent : IStudent
        {
            public string Name { get; set; }
    
            [Dependency("ec")]
            public IClass ToClass { get; set; }
    
            public void ShowInfo()
            {
                Console.WriteLine("{0}就读班级:{1}", Name, ToClass.ClassName);
            }
        }
    复制代码
    复制代码

    代码方式如下:

                IUnityContainer container = new UnityContainer();
                container.RegisterType<IClass, EcClass>("ec");         
                container.RegisterType<IStudent, QlinStudent>();
                IStudent splitClass = container.Resolve<IStudent>();
                splitClass.ShowInfo();

    配置文件方式,依赖的<dependency name="ec1" name值 可指定注册时注册的名称:

    复制代码
    复制代码
    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <!--引用命名空间-->
        <namespace name="ConsoleApplication1.UnityDemo.Constructor4" />
        <!--引用程序集-->
        <assembly name="ConsoleApplication1" />
        <!--容器-->
        <container name="FirstClass">
          <!--映射关系-->
          <register type="IClass"  mapTo="CbClass">      
          </register>
          <register type="IClass" name="ec1" mapTo="EcClass">
            <property name="ClassName" propertyType="System.String" value="电商051" />
          </register>
          <register type="IStudent"  mapTo="QlinStudent">
            <property name="ToClass">
              <dependency name="ec1" type="EcClass"/>
            </property>
          </register>
        </container>
      </unity>
    复制代码
    复制代码

    调用效果图:

    4.方法注入

    用public修饰方法,方法注入也是跟构造函数类似代码修改如下

    复制代码
    复制代码
        public class QlinStudent : IStudent
        {
            public string Name { get; set; }
    
            private IClass ToClass { get; set; }
    
            [InjectionMethod]
            public void InitClass(IClass _class)
            {
                ToClass = _class;
            }
    
            public void ShowInfo()
            {
                Console.WriteLine("{0}就读班级:{1}", Name, ToClass.ClassName);
            }
        }
    复制代码
    复制代码

    编程方式注入不变,就是初始化时,注入值,如下:

                IUnityContainer container = new UnityContainer();
                container.RegisterType<IClass, EcClass>();         
                container.RegisterType<IStudent, QlinStudent>();
                IStudent student = container.Resolve<IStudent>();
                student.ShowInfo();

    配置文件方式:

    复制代码
    复制代码
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <!--引用命名空间-->
        <namespace name="ConsoleApplication1.UnityDemo.Constructor5" />
        <!--引用程序集-->
        <assembly name="ConsoleApplication1" />
        <!--容器-->
        <container name="FirstClass">
          <!--映射关系-->
          <register type="IClass"  mapTo="CbClass">      
          </register>
          <register type="IClass" name="ec1" mapTo="EcClass">
            <property name="ClassName" propertyType="System.String" value="电商051" />
          </register>
          <register type="IStudent"  mapTo="QlinStudent">
            <property name="Name" propertyType="System.String" value="Qlin" />
            <method name="InitClass">
              <param name="_class" type="IClass">
                <dependency name="ec1" type="EcClass"/>
              </param>
            </method>
          </register>
        </container>
      </unity>
    复制代码
    复制代码

    5.小结

     介绍了3种依赖注入方式,平时主要也就用到这么几种,其它还有复杂的像扩展容器等,通过本节,基本知道Unity的使用了。

  • 相关阅读:
    codevs 1115 开心的金明
    POJ 1125 Stockbroker Grapevine
    POJ 2421 constructing roads
    codevs 1390 回文平方数 USACO
    codevs 1131 统计单词数 2011年NOIP全国联赛普及组
    codevs 1313 质因数分解
    洛谷 绕钉子的长绳子
    洛谷 P1276 校门外的树(增强版)
    codevs 2627 村村通
    codevs 1191 数轴染色
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2923478.html
Copyright © 2011-2022 走看看