• Autofac


    Autofac

    什么是IOC

    控制反转(inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)

    IOC: 调用者不再创建(不自己new) 被调用者的实例,而是交给容器去创建,这就是控制反转.
    DI:容器创建好的实例再注入调用者的过程,就是依赖注入. (比如属性入住构造函数注入等.)

    为什么要使用IOC

    我们日常使用的设计模式多为接口驱动设计,接口驱动设计有很多好处,可以提供不同灵活的子类实现的,增加代码稳定和健壮性等等.

    但是接口一定需要实现的,也就是如下语句迟早要执行:
    AInterface 啊= new AInterfaceImp();
    所以,按照一般的方法使用接口,即会出现耦合.

    构建一个简单的依赖注入的例子

    创建服务(接口)

    public interface ITestServices
    {
            int Count { get; }
            void Add();
    }

    创建组件(实现类)

    public class TestServicesImpl : ITestServices
    {
            private int _count;
    
            public TestServicesImpl()
            {
                _count = 0;
            }
    
            public int Count => this._count;
    
            public void Add()
            {
                _count++;
            }
    }

    创建控制器

    [Route("api/TestServices")]
    [ApiController]
        public class TestServicesController:ControllerBase
        {
            private readonly ITestServices _testServices;
    
           // 通过构造函数的形式获取ITestServices服务的注册实例
            public TestServicesController(ITestServices testServices)
            {
                _testServices = testServices;
            }
    
            [HttpGet]
            public Tuple<int, int> Get()
            {
                var before = this._testServices.Count;   //拿到老的统计值
                this._testServices.Add();                       //自增一个统计值
                var after = this._testServices.Count;      //获取新的统计值
                return new Tuple<int, int>(before,after);//返回老的统计值和新的统计值
            }
        }

    在 .Net Core 自带的注入服务的方法中,使用 .Net Core 自带的注入服务进行组件注入

    
    // 在 StartUp类中的  ConfigureServices 方法中进行注入
     public void ConfigureServices(IServiceCollection services)
    {
                services.AddControllers();
    
                //将组件TestServicesImpl,注册给ITestServices 服务
                services.AddScoped<ITestServices, TestServicesImpl>();  
    }

    IoC 是如何降低耦合的

    Class A中用到了Class B 的对象b,一般情况下,需要在A的代码中显式的new一个B的对象.

    采用依赖注入技术之后,A的代码值需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类的引用中.二集体获取的方法,对象被获取时的状态由配置(如XML)来指定.

    容器

    Ioc 模式,系统中通过引入实现了Ioc模式的Ioc容器,即可由Ioc容器来管理对象的生命周期,依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分离.其中一个特点就是通过配置进行应用程序组件间相互关系的配置,而不用重新修改并编写具体的代码.

    .Net Core 的常用容器

    1. .Net Core 自带容器
    2. Autofac

    .Net 自带容器的使用说明

    .Net Core 自带容器的注入一般在Startup中的 public void ConfigureServices(IServiceCollection services) 方法中进行. 根据生命周期区分为三种.

    1. AddTransient<服务,组件>(); //瞬时的,每次使用都会为服务注入一个新的实例.
    2. AddScoped<服务,组件>(); //一次http请求之后使用一个实例,即任何地方调用都是同一个对象.
    3. AddSingle<服务,组件>(); //单例的,在第一次http请求之后会生成一个实例,并且此实例会一直被使用到 .net core 程序生命周期结束,即全局单例.

    在 .Net Core 中如果要在Action中,拿到来自注入的实例对象,我们需要使用如下语句,我们可以注意到,在形参中,加入了[FromServices]的标注.

    [HttpGet]
    public Tuple<int, int> Get([FromServices] ITestServices testServices)
    {
                var before = this._testServices.Count;
                this._testServices.Add();
                var after = this._testServices.Count;
    
                return new Tuple<int, int>(before,after);
    }

    AutoFac

    组件: 一串声明了它所提供服务和它所消费依赖的代码(类,对象)
    服务: 一个在提供和消费组件之间明确定义的行为约定(接口)

    Autofac 反射组件

    RegisterType:注册一个类.
    As:将类注册为此接口.
    SingleInstance:单例,全局只有一个.
    InstanPerLifetimeScope: 在同作用域,服务每次请求只创建一次.

    .Net Core 中使用 Autofac 进行依赖注入

    我们需要在Nuget上先安装Autofac.Extensions.DependencyInjection,然后我们需要在Program.cs类中,去修改CreateHostBuilder方法,目的是添加Autofac依赖注入容器.

    通过Nuget安装Autofac
    通过Nuget安装Autofac

    //原代码
     public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                       .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });
    
    //新代码
     public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                       .UseServiceProviderFactory(new AutofacServiceProviderFactory())  //添加Autofac 容器
                       .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });

    我们还需要在StartUp.cs类中添加对Autofac容器进行配置的函数,此处我们需要注意函数的命名规范.

        /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
                //(注册组件)具体实现的注入(在后续使用中只能对在构造区中定义TestServicesImpl类型的形参进行注入),且不是一次请求只产生一个实例对象,凡是需要注入的地方,都会产生一个新的对象. 
                builder.RegisterType<TestServicesImpl>();    
            }
    /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
               
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //且不是一次请求只产生一个实例对象,凡是需要注入的地方,都会产生一个新的对象.
                builder.RegisterType<TestServicesImpl>().As<ITestServices>();    
            }
     /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //全局单例
                builder.RegisterType<TestServicesImpl>().As<ITestServices>().SingleInstance();
            }
     /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //一次http请求共用一个注入的对象实例
                builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();
            }

    以上是,我们通过Autofac实现了, .Net core自带的注入功能,下面我们了解一下Autofac 自己的一些功能.

    Autofac指定构造函数

    指定构造函数
    我们可以通过使用UseingConstructor来指定构造函数来进行注册,此处需要注意一下,如果不能不指定构造函数来进行构造,那么Autofac默认使用形参最多的构造函数来进行实例注入.

    声明服务

    public interface IConstructor
        {
             ITestServices Services { get; }
        }

    声明组件

    public class ConstructorImpl:IConstructor
        {
            public ConstructorImpl()
            {
                Console.WriteLine("无参的构造函数");
            }
    
            private readonly ITestServices _testServices;
    
            public ConstructorImpl(ITestServices testServices)
            {
                _testServices = testServices;
    
                Console.WriteLine("有一个参数的构造函数");
            }
    
            public ITestServices Services => this._testServices;
        }

    配置注入

    /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
              
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //一次http请求共用一个注入的对象实例
                builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();
    
                //不指定构造函数,进行注入时,Autofac默认会使用形参最多的构造函数进行注入.
                builder.RegisterType<ConstructorImpl>().As<IConstructor>();
    
            }

    依赖注入

    [Route("api/Autofac")]
        [ApiController]
        public class AutofacController
        {
            private readonly ITestServices _testServices;
            private readonly IConstructor _constructor;
    
            public AutofacController(ITestServices testServices,IConstructor constructor)
            {
                _testServices = testServices;
                _constructor = constructor;
            }
    
            [HttpGet]
            public Tuple<int, int> Get([FromServices] ITestServices testServices)
            {
                if (this._testServices.GetHashCode() == testServices.GetHashCode())
                {
                    Console.WriteLine(true);
                }
                else
                {
                    Console.WriteLine(false);
                }
                var before = this._testServices.Count;
                this._testServices.Add();
                var after = this._testServices.Count;
    
                return new Tuple<int, int>(before, after);
            }
        }

    调用结果:

    执行结果
    执行结果

    通过上述结果可知,Autofac在没有指定构造函数注入时,默认使用形参最多的构造函数进行注入.

    使用无参的构造函数进行注入:

     /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
                //使用无参的构造函数进行注入
                builder.RegisterType<ConstructorImpl>().As<IConstructor>().UsingConstructor();
            }

    调用结果:

    执行结果
    执行结果

    Autofac 实例组件

    我们可以通过使用 RegisterInstance 来指定实例来进行注册,如果想自己来控制组件的生存周期,可以使用ExternallyOwned

       /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
           
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //一次http请求共用一个注入的对象实例
                builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();
    
                ////给指定服务注册已有实例 后续使用中,使用的都是同一个实例,即下面new出的实例.
                var instance = new ConstructorImpl();
                builder.RegisterInstance(instance).As<IConstructor>();
            }

    Autofac Lambda表达式组件

    1. 可以使用Lambda表达式来进行注入.
    2. 使用Lambda表达式时可以进行复杂参数的使用.
         /// <summary>
            /// Autofac容器配置函数
            /// </summary>
            public void ConfigureContainer(ContainerBuilder builder)
            {
              
                //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
                //一次http请求共用一个注入的对象实例
                builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();
    
                //使用Lambda表达式进行注入,此处的c表示容器,此处需要注意的是c.Resolve<组件> 这段代码中,此组件必须在下列这行代码被
                //执行前,就已经注册,否则下面的代码就会报错.
                builder.Register(c => new ConstructorImpl(c.Resolve<ITestServices>())).As<IConstructor>();
            }

    Autofac 通过参数选择实现

    1. 注册时可以通过parameter参数选择具体的判定参数
    2. 通过ILifetimeScope容器来获取实现.

    以上的例子都是一个接口只有一个类型的实现,那么我们一个接口有多种实现类,那么我们是不是可以通过参数,来选择使用某种具体的实现呢?

    声明服务

       public interface IPersonService
        {
            string Sex { get; }
        }

    声明组件1

    public class PersonManImpl : IPersonService
        {
            public string Sex => "男人";
        }

    声明组件2

    public class PersonWoManImpl:IPersonService
        {
    
            public string Sex => "女人";
        }

    依赖注入

     /// <summary>
     /// Autofac容器配置函数
     /// </summary>
     public void ConfigureContainer(ContainerBuilder builder)
     {
        
         //根据请求参数使用具体的实现类
         //此处c表示容器,p表示参数
         builder.Register<IPersonService>((c, p) =>
         {
             var sex = p.Named<string>("sex");  //通过名称参数来选择具体的实现类
             if (sex == "1")    //1表示男人
             {
                 return new PersonManImpl();
             }
             else 
             {
                 return new PersonWoManImpl();
             }
         });
     }

    调用url : http://localhost:5000/api/autofac/1

    调用

    [Route("api/Autofac")]
    [ApiController]
    public class AutofacController
    {
        private readonly ILifetimeScope _lifetimeScope;  //保存Autofac容器本身
    
        public AutofacController( ILifetimeScope lifetimeScope)
        {
            _lifetimeScope = lifetimeScope;
        }
    
    
        [HttpGet("{n}")]
        public string GetSex(string n)
        {
            //通过传入的参数来区分到底使用哪个具体的实现.
            var person = this._lifetimeScope.Resolve<IPersonService>(new NamedParameter("sex", n));
            return person.Sex;
        }
    }

    返回结果:

    执行结果
    执行结果

    Autofac 程序集扫描

    1. RegisterAssemblyTypes:扫描类型.
    2. publicOnly:仅注册公开类型.
    3. Where通过条件过滤.
    4. Except:排除某个类型.

    下面我们使用一个CRUD的过程来介绍一下如何使用Autofac.

    首先我们需要先通过Nuget来安装两个依赖包.

    1. Autofac Autofac主程序包
    2. Autofac.Extensions.DependencyIniection Autofac扩展的关于DI的包.

    通过Nuget安装
    通过Nuget安装

    在 Porgram.cs 中先添加Autofac容器的支持.

     public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .UseServiceProviderFactory(new AutofacServiceProviderFactory())  //添加Autofac 容器
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });

    在 StartUp.cs 中添加 Autofac 容器的配置函数

    /// <summary>
    /// Autofac 容器配置函数
    /// </summary>
    /// <param name="builder"></param>
    public void ConfigureContainer(ContainerBuilder builder)
    {
                // 获取当前应用程序的所在的程序集
                var curAssembly = Assembly.GetExecutingAssembly();
    
                // 通过名称来筛选实现, 此处因为我们所有的实现都是以"Impl"来结尾的,所有我们可以通过这个来筛选,并将这些组件,注册给他们自己的服务
                // 此处的t指的是type
                //builder.RegisterAssemblyTypes(curAssembly)
                //       .Where(t => t.Name.EndsWith("Impl"))
                //       .AsImplementedInterfaces();
    
                //我们也可以将上诉程序集中的所有实现都注册成单例的形式,即
                //builder.RegisterAssemblyTypes(curAssembly)
                //    .Where(t => t.Name.EndsWith("Impl"))
                //    .AsImplementedInterfaces().SingleInstance();
    
                //我们也可以指定某一个服务,及其实现是单例形式的,代码如下
                builder.RegisterAssemblyTypes(curAssembly)
                    .Where(t => t.Name.EndsWith("Impl") && t.Name != "RepositoryImpl")  //过滤处RepositoryImpl
                    .AsImplementedInterfaces().SingleInstance();
    
                //上面的所有已Impl结尾的都是单例的除了(RepositoryImpl),下面是挑出不想是单例的实现,单独注册
                builder.RegisterAssemblyTypes(curAssembly)
                    .Where(t => t.Name.EndsWith("RepositoryImpl"))
                    .AsImplementedInterfaces();
    
    
                    // 其它功能请参阅 Aufofac文档
     }

    下面我们使用仓储模式简单的进行设计一下.

    创建仓储接口(服务):

    using System.Collections.Generic;
    using MyIoCDemo.Model;
    
    namespace MyIoCDemo.Repository
    {
        public interface IUserRepository
        {
            IEnumerable<UserModel> GetUsers();
            UserModel GetUser(int id);
            void AddOrUpdateUser(UserModel user);
            void DeleteUser(int id);
        }
    }

    创建数据模型

    namespace MyIoCDemo.Model
    {
        public class UserModel
        {
            public int Id { get; set; }
            public int Age { get; set; }
            public string Name { get; set; }
            public string Phone { get; set; }
    
        }
    }

    创建仓储实现类(组件):

    using System.Collections.Generic;
    using System.Linq;
    using MyIoCDemo.Model;
    
    namespace MyIoCDemo.Repository
    {
        public class UserRepositoryImpl:IUserRepository
        {
            private List<UserModel> _users = new List<UserModel>
            {
                new UserModel
                {
                    Id = 1,Age = 20,Name = "张三",Phone="1234567"
                },
                new UserModel
                {
                    Id = 2,Age = 21,Name = "李四",Phone="1234568"
                },
                new UserModel
                {
                    Id = 3,Age = 22,Name = "王五",Phone="1234569"
                }
            };
    
            public IEnumerable<UserModel> GetUsers()
            {
                return this._users;
            }
    
            public UserModel GetUser(int id)
            {
                var item = this._users.Find(u => u.Id == id);
                return item;
            }
    
            public void AddOrUpdateUser(UserModel user)
            {
                var item = this._users.Where(u => u.Id == user.Id).FirstOrDefault();
                if (item == null)
                {
                    //添加
                    this._users.Add(user);
                }
                else
                {
                    //更新
                    this._users.Remove(item);
                    this._users.Add(user);
                }
            }
    
            public void DeleteUser(int id)
            {
                var item = this._users.Where(u => u.Id == id).FirstOrDefault();
                if (item != null)
                {
                    //删除
                    this._users.Remove(item);
                }
               
            }
        }
    }

    创建用户接口(服务):

    using System.Collections.Generic;
    using MyIoCDemo.Model;
    
    namespace MyIoCDemo.Services
    {
        public interface IUserServices
        {
            IEnumerable<UserModel> GetUsers();
            UserModel GetUser(int id);
            void AddOrUpdateUser(UserModel user);
            void DeleteUser(int id);
        }
    }

    创建用户实现类(组件):

    using System.Collections.Generic;
    using MyIoCDemo.Model;
    using MyIoCDemo.Repository;
    
    namespace MyIoCDemo.Services
    {
        public class UserServicesImpl:IUserServices
        {
                                  
            private readonly IUserRepository _repository;
    
            public UserServicesImpl(IUserRepository repository)
            {
                _repository = repository;
            }
    
            public IEnumerable<UserModel> GetUsers()
            {
                return this._repository.GetUsers();
            }
    
            public UserModel GetUser(int id)
            {
                return this._repository.GetUser(id);
            }
    
            public void AddOrUpdateUser(UserModel user)
            {
                this._repository.AddOrUpdateUser(user);
            }
    
            public void DeleteUser(int id)
            {
                this._repository.DeleteUser(id);
            }
        }
    }

    控制器:

    using System.Collections.Generic;
    using System.Runtime.InteropServices.ComTypes;
    using Microsoft.AspNetCore.Mvc;
    using MyIoCDemo.Model;
    using MyIoCDemo.Services;
    
    namespace MyIoCDemo.Controllers
    {
        [Route("api/User")]
        [ApiController]
        public class UserController:ControllerBase
        {
            private readonly IUserServices _userServices;
    
            public UserController(IUserServices userServices)
            {
                _userServices = userServices;
            }
    
            /// <summary>
            /// 获取所有员工
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public IEnumerable<UserModel> Get()
            {
                return this._userServices.GetUsers();
            }
    
            /// <summary>
            /// 获取指定员工
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            [HttpGet("{id}")]
            public UserModel Get(int id)
            {
                return this._userServices.GetUser(id);
            }
    
            /// <summary>
            /// 添加
            /// </summary>
            /// <param name="model"></param>
            [HttpPost]
            public void Post([FromBody]UserModel model)
            {
                this._userServices.AddOrUpdateUser(model);
            }
    
            /// <summary>
            /// 修改
            /// </summary>
            /// <param name="id"></param>
            /// <param name="value"></param>
            [HttpPut("{id}")]
            public void Put(int id, [FromBody] UserModel value)
            {
                value.Id = id;
                this._userServices.AddOrUpdateUser(value);
            }
    
            /// <summary>
            /// 删除
            /// </summary>
            /// <param name="id"></param>
            [HttpDelete("{id}")]
            public void Delete(int id)
            {
                this._userServices.DeleteUser(id);
            }
        }
    }

    以上例子参考自:

    .Net Core 依赖注入基础
    .Net Core Autofac 基础1
    .Net Core Autofac 基础2

  • 相关阅读:
    struts-OGNL
    Linux开发环境配置大全
    Mybartis逆向工程
    开发环境配置大全
    金三银四,你的专属复习宝典
    Java5~11新特性
    Struts2+Spring+Hibernate整合开发(Maven多模块搭建)
    三层架构,Struts2,SpringMVC实现原理图
    Springmvc+Spring+Mybatis整合开发(架构搭建)
    MyBatis面试题整理
  • 原文地址:https://www.cnblogs.com/HelloZyjS/p/12560593.html
Copyright © 2020-2023  润新知