c#中的IOC容器之Autofac容器

IOC(inversion of control)控制反转

以前我们把框架分为三层架构:UI层,BLL层,DAL层。在三层架构中我们,我们是一层一层的调用对象里面的方法,需要实例化对象:类名  对象名 = new 类名();这种叫传统工艺。
现在我们不需要在依赖某一个类来获取到实例。而是依赖于抽象,怎么获取?交给了第三方(称:IOC容器)通过抽象(接口,抽象类,普通父类)的方式获取到类的实例。
IOC内置容器:ServiceCollection
1.创建库类接口【Interfaces】里面什么都不写,只是做依赖注入的关系

public interface IMicrophone{ }

2.创建一个服务类库【Services】做我们需要调用的具体对象

public class Microphone: IMicrophone
{
     public Microphone() { Console.WriteLine($"{this.GetType().Name}被构造了"); }
}

3.通过容器依赖注入到项目中来

using Interfaces;//接口类库
using Services;//服务类库
using Microsoft.Extensions.DependencyInjection;//内置IOC容器

ServiceCollection service = new ServiceCollection();//1.创建一个容器,需要nuget引入:Microsoft.Extensions.DependencyInjection
service.AddTransient<IMicrophone, Microphone>();//2.建立关系或是注册抽象和具体普通类之间的关系,注册方式有很多,这里只是其中一种
ServiceProvider sp = service.BuildServiceProvider();//3.进行集中管理,统一注入到ServiceProvider,而BuildServiceProvider就是获取依赖关系
IMicrophone microphone= sp.GetService<IMicrophone>();//4.获取抽象的具体实例,就相当于Microphone被实例化后的具体对象,通过接口抽象出来的

传统工艺:new 实例化对象。对象创建在项目中是多个地方的。
ioc容器可以在全局,配置抽象和具体普通类之间的关系,是可以修改AddTransient<抽象, 具体类>();修改为什么获取的就是什么。
ioc容器,最大的好处在于DI依赖注入:如果A依赖于B,B依赖于C;那么在创建A时,会把C先创建出来交给B,在把B创建出来交给A,从而创建出A对象来。

public interface IHeadphone{ }//接口

public class Headphone : IHeadphone //依赖于IPower
{
    public Headphone(IPower power) { Console.WriteLine($"{this.GetType().Name}被构造了"); }
}

Power依赖于IMicrophone

public interface IPower { } //接口

public class Power : IPower
{
    public Power(IMicrophone microphone) { Console.WriteLine($"{this.GetType().Name}被构造了"); }
}

Microphone有继承了IMicrophone

public interface IMicrophone{ }//接口

public class Microphone: IMicrophone
{
    public Microphone() { Console.WriteLine($"{this.GetType().Name}被构造了"); }
}

这种依赖注入关系叫构造函数注入

using Interfaces;//接口类库
using Services;//服务类库
using Microsoft.Extensions.DependencyInjection;//内置IOC容器

Console.WriteLine("传统工艺,实例化对象-------------------");
IMicrophone m =new Microphone();
IPower p = new Power(m);
IHeadphone h = new Headphone(p);

Console.WriteLine("IOC容器支持的依赖注入-----------------------");
ServiceCollection service = new ServiceCollection();//1.创建容器:ioc容器本质就是个工厂用来创建对象的
service.AddTransient<IMicrophone, Microphone>();//2.建立关系
service.AddTransient<IPower, Power>();
service.AddTransient<IHeadphone, Headphone>();
ServiceProvider sp = service.BuildServiceProvider();//3.进行集中管理,获取依赖关系
IHeadphone headphone = sp.GetService<IHeadphone>();//4.获取实例

依赖注入有三种【1.构造函数注入;2.方法注入;3.属性注入】但内置ioc容器只支持构造函数注入,其他两种只能去扩展
ServiceCollection的生命周期

ServiceCollection service = new ServiceCollection();
service.AddTransient<IMicrophone, Microphone>();//Transient:瞬时生命周期,每一次创建都是新的实例
service.AddSingleton<IHeadphone, Headphone>();//Singleton:单例的生命周期,相同的一个引用地址,同一对象实例
service.AddScoped<IPower, Power>();//Scoped:作用域生命周期,保存在相同的ServiceProvider对象里,获取是相同的实例,反之不同
ServiceProvider sp = service.BuildServiceProvider();//获取依赖关系保存到对象
IMicrophone m1 = sp.GetService<IMicrophone>(); 
IMicrophone m2 = sp.GetService<IMicrophone>();
Console.WriteLine($"Transient:瞬时生命周期比较m1和m2是否一样:{object.ReferenceEquals(m1,m2)}");
IHeadphone h1 = sp.GetService<IHeadphone>();
IHeadphone h2 = sp.GetService<IHeadphone>();
Console.WriteLine($"Singleton:单例生命周期比较h1和h2是否一样:{object.ReferenceEquals(h1, h2)}");
IPower p1 = sp.GetService<IPower>();
IPower p2 = sp.GetService<IPower>();
Console.WriteLine($"Scoped:作用域生命周期比较p1和p2是否一样:{object.ReferenceEquals(p1, p2)}");

ServiceCollection的多种注册:上面使用的是泛型注册

ServiceCollection service = new ServiceCollection();
service.AddTransient(typeof(IMicrophone), typeof(Microphone));//方法注册和泛型注册一样的
service.AddTransient(typeof(Microphone));//可以直接给个具体实例,调用也是实例
service.AddTransient<Microphone>();//泛型注册,也可以直接给一个实例
service.AddTransient(typeof(IPower), a => { //委托,注册抽象和一段业务逻辑
    //在这里我们可以直接来决定创建这个对象的实例,可以对这个实例做加工。
    IMicrophone m = a.GetService<IMicrophone>();
    return new Power(m);
});
ServiceProvider sp = service.BuildServiceProvider();
IMicrophone m1 = sp.GetService<IMicrophone>();
Microphone m2 = sp.GetService<Microphone>();//泛型注册也是一样的,可以直接给一个实例
IPower p = sp.GetService<IPower>();

ServiceCollection容器在asp.net core中的使用:首先在【Program.cs】

builder.Services.AddTransient<IMicrophone, Microphone>();//依赖:在顶级程序中注册内置ioc容器。

注入:到控制器(构造方法)中注入使用

private readonly IMicrophone microphone;public HomeController(IMicrophone microphone, IServiceProvider sp)//home控制器的构造方法
{
    this.microphone = microphone;//方法1:注入进来的ioc容器
    this.microphone = sp.GetService<IMicrophone>();//方法2::这两种获取方式是一样
}

Autofac

新的容器替代内置容器,需要引入:Autofac

ContainerBuilder containerBuilder = new ContainerBuilder();//实例化对象ioc容器,需要引入:Autofac
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();//配置关系
IContainer container = containerBuilder.Build();//获取关系存放在集合中
IMicrophone microphone = container.Resolve<IMicrophone>();//获取实例

多种注册

ContainerBuilder containerBuilder = new ContainerBuilder();//创建:ioc容器对象
containerBuilder.RegisterInstance(new Microphone());//方法1,:注册一个具体实例
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();//方法2:建立接口和实例的关系,然后委托一段业务逻辑
containerBuilder.Register<IPower>(context => {//委托,注册一段业务逻辑
    //这里的业务逻辑创建出IPower的实例----给一个入口,自己创建出对象的实例
    IMicrophone microphone = context.Resolve<IMicrophone>();
    IPower power = new Power(microphone);
    return power;
});
containerBuilder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>));//方法3:注册泛型

IContainer container = containerBuilder.Build();//获取关系,保存到集合

IMicrophone microphone = container.Resolve<IMicrophone>();//1.接口和实例关系创建的
IPower power = container.Resolve<IPower>();//2.注册一段业务逻辑
IList<IMicrophone> mlist = container.Resolve<IList<IMicrophone>>();//3.注册泛型

Assembly程序集注册,需要引入:System.Reflection;

using Interfaces;//接口类库
using Services;//服务类库
using Autofac;//ioc容器
using System.Reflection;//Assembly获取程序集

ContainerBuilder containerBuilder = new ContainerBuilder();//创建:ioc容器对象
Assembly InterfaceAssembly = Assembly.LoadFrom("Interfaces.dll");//接口程序集
Assembly serviceAssembly = Assembly.LoadFrom("Services.dll");//实例类程序集
containerBuilder.RegisterAssemblyTypes(InterfaceAssembly, serviceAssembly).AsImplementedInterfaces();//建立程序集关系
IContainer container = containerBuilder.Build();//获取关系,保存到集合
IEnumerable<IMicrophone> microphone = container.Resolve<IEnumerable<IMicrophone>>();//泛型实例,普通实例也可以

依赖注入:依赖指的是上面的ioc容器建立的依赖关系;注入【1.构造函数注入,2.方法注入,3.属性注入】
1.构造函数注入:UsingConstructor( )

public class Power : IPower
{
    private readonly IMicrophone microphone;//通过ioc容器注入进来的实例
    public Power(IMicrophone microphone)
    { //这种叫构造函数注入this.microphone = microphone;
    }
}

如果有多个构造函数【默认选择参数最多的构造函数】如果需要指定执行的构造函数,注册时需要调用:UsingConstructor( ) 方法来决定执行哪个构造函数

ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();//UsingConstructor指定多少参数就能行哪个构造函数,默认执行最多参数的构造函数
containerBuilder.RegisterType<Power>().As<IPower>().UsingConstructor(typeof(IMicrophone));//指定执行的构造函数必须只有一个参数类型为:IMicrophone
IContainer container = containerBuilder.Build();
IPower microphone = container.Resolve<IPower>();

2.属性注入:PropertiesAutowired() ----------所有属性都注入

ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();
containerBuilder.RegisterType<Power>().As<IPower>().PropertiesAutowired();//指定:属性注入
IContainer container = containerBuilder.Build();
IPower microphone = container.Resolve<IPower>();
public class Power : IPower
{
    public IMicrophone microphone { get; set; }//属性注入直接传进属性来的
    public Power() //参数不需要传
    {
    }
}

指定属性注入(PropertySelector接口):首先创建一个【特性】用于做属性注入的标记,哪个属性标记了这个特性,就代表这个属性支持属性注入,否则就是普通属性

[AttributeUsage(AttributeTargets.Property)]//声明特性只能标记在属性上面有效
public class CusotmSelectAttribute : Attribute { }//做标记;表示谁使用了这个特性谁就支持属性注入,否则就是普通属性

public class CusotmPropertySelector : IPropertySelector
{//在这里面判断哪些属性是需要做属性注入
    public bool InjectProperty(PropertyInfo propertyInfo, object instance)
    {
        return propertyInfo.CustomAttributes.Any(c => c.AttributeType == typeof(CusotmSelectAttribute));//标记了这个特性,就需要实现注入
    }
}

在注册依赖关系时让PropertySelector生效

containerBuilder.RegisterType<Power>().As<IPower>().PropertiesAutowired(new CusotmPropertySelector());//指定属性注入

特性的引用

public class Power : IPower
{
    [CusotmSelectAttribute]//指定了属性注入
    public IMicrophone microphone { get; set; }//这个是注入后的实例
    public IHeadphone headphone { get; set; }//就是个普通属性
    public Power() //参数不需要传
    {
    }
}

 3、方法注入:OnActivated(委托);在构造出某一个对象后,自动去调用对象的某一个方法,方法需要什么参数,自动构造出来,传入参数。

ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();
containerBuilder.RegisterType<Power>().As<IPower>().OnActivated(active => { //指定字段与方法行成依赖关系
    IMicrophone micro = active.Context.Resolve<IMicrophone>();//Context从上下文中获取到实例
    active.Instance.index111(micro);//使用index111方法
});
IContainer container = containerBuilder.Build();
IPower microphone = container.Resolve<IPower>();
public class Power : IPower
{
    public IMicrophone _microphone;//字段必须是公开的public
    public Power() //参数不需要传,同时也支持构造方法注入,传进来也一样可以获取
    {
    }
    public void index111(IMicrophone microphone)//和构造方法注入类似
    {
        this._microphone = microphone;
    }
}

单抽象多实现:默认创建出的对象,是最后注册的服务关系。

ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Microphone>().As<IMicrophone>();//Microphone继承接口IMicrophone
containerBuilder.RegisterType<Headphone>().As<IMicrophone>();//Headphone也继承接口IMicrophone
IContainer container = containerBuilder.Build();
IMicrophone microphone = container.Resolve<IMicrophone>();//这么默认获取到最后一个实例,也就是Headphone
IEnumerable<IMicrophone> m = container.Resolve<IEnumerable<IMicrophone>>();//想获取多个就使用可迭代泛型

如果需要获取某个实例,可以指定一个标识Keyed

ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Microphone>().Keyed<IMicrophone>("Microphone");//指定一个标识
containerBuilder.RegisterType<Headphone>().Keyed<IMicrophone>("Headphone");
IContainer container = containerBuilder.Build();
IMicrophone m = container.ResolveKeyed<IMicrophone>("Microphone");//获取标识
IMicrophone h = container.ResolveKeyed<IMicrophone>("Headphone");//如果是集合获取也需要指定标识ResolveKeyed

 想在mvc里使用Autofac还需要引用:Autofac.Extensions.DependencyInjection 替换内置工厂

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());//1.通过工厂替换为Autofac(把内置的AOP替换掉)
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { //2.创建一个工厂
    containerBuilder.RegisterType<Microphone>().As<IMicrophone>();//3.注册关系
});

控制器中构造方法调用(3种注入方式)

private readonly IMicrophone microphone;
private readonly IEnumerable<IMicrophone> list;
private readonly IServiceProvider sp;

public HomeController(IMicrophone microphone, IEnumerable<IMicrophone> list, IServiceProvider sp, IComponentContext componentContext)
{
    this.microphone = microphone;
    this.sp = sp;
    this.list = list;
    IMicrophone m = sp.GetService<IMicrophone>();
    IMicrophone m2 = componentContext.Resolve<IMicrophone>();//Autofac的上下文也可以注入
}

 属性注入(特性上面写了就不写了)

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { 
    containerBuilder.RegisterType<Microphone>().As<IMicrophone>();

    //2.注册某个控制器和抽象之间的关系
    var controllerBaseType = typeof(ControllerBase);//控制器都继承他,借用这个父类来识别是否是控制器。
    containerBuilder.RegisterAssemblyTypes(typeof(Program).Assembly)//注册程序集(当前控制台所在的程序集)
    .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)//找到的是控制器,ControllerBase类型,但又不等于他
    .PropertiesAutowired(new CusotmPropertySelector());//支持属性注入,这里就可以在属性上面标注特性了。
});
//1.支持控制器的这个实例让ioc容器来创建---其实就是让Autofac来创建
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());//扩展作用是替换(参数1的事交给参数2来处理)
[CusotmSelectAttribute]
public IMicrophone microphone { get; set; }

三、Autofac支持AOP

需要先引入程序包:Castle.Core 还需要引入:Autofac.Extras.DynamicProxy 来支持AOP扩展

public class CusotmInterceptor : IInterceptor//切入者逻辑
{//这个是Autofac支持AOP的扩展方法,需要引入:Castle.Core
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("call方法之前执行");
        invocation.Proceed();//执行call方法
        Console.WriteLine("call方法之后执行");
    }
}

注册时调用:EnableClassInterceptors() 方法他们叫拦截器--------标记在类中,只对虚方法有效

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => {
    containerBuilder.RegisterType<Microphone>().As<IMicrophone>().EnableClassInterceptors();//支持aop扩展必须调用此方法。只会拦截虚方法。
    containerBuilder.RegisterType<CusotmInterceptor>();//把aop扩展方法也注入进来才能使用,里面写方法前后执行动作。
});
using Autofac.Extras.DynamicProxy;
using Interfaces;
namespace Services
{
    [Intercept(typeof(CusotmInterceptor))]//指定被aop扩展,EnableClassInterceptors标记在类中,只对类中的虚方法有有效
    public class Microphone: IMicrophone
    {
        public Microphone() { Console.WriteLine($"{this.GetType().Name}被构造了"); }
        public virtual void call() { Console.WriteLine("call方法以执行,必须定义为虚方法才支持aop扩展。"); }
        public void ca() { Console.WriteLine("普通方法不是虚方法,不支持aop扩展。"); }//实现接口的两个方法
    }
}

EnableInterfaceInterceptors()-----------------------------标记在接口,对私有实现接口方法有效。

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => {
    containerBuilder.RegisterType<Microphone>().As<IMicrophone>().EnableInterfaceInterceptors();//标记在接口,对私有实现接口方法有效。
    containerBuilder.RegisterType<CusotmInterceptor>();//把aop扩展方法也注入进来才能使用,里面写方法前后执行动作。
});
using Autofac.Extras.DynamicProxy;
namespace Interfaces
{
    [Intercept(typeof(CusotmInterceptor))]//EnableInterfaceInterceptors()标记在接口,对私有实现接口方法有效
    public interface IMicrophone{
        void call();
        void ca();
    }
}

控制台的注入

public class HomeController : Controller
{
    private readonly IMicrophone microphone;
    public HomeController(IMicrophone microphone)
    {
        this.microphone = microphone;
    }
    public IActionResult Index()
    {
        microphone.call();//虚方法
        microphone.ca();//普通方法

        return View();
    }
}

 在项目中可以做为日志记录,记录方法的动态变化

using Castle.Core.Logging;
using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;

namespace Interfaces
{
    public class CusotmInterceptor : IInterceptor//切入者逻辑
    {//这个是Autofac支持AOP的扩展方法,需要引入:Castle.Core
        private readonly ILogger<CusotmInterceptor> logger;

        public CusotmInterceptor(ILogger<CusotmInterceptor> logger)
        {
            this.logger = logger;
        }
        public void Intercept(IInvocation invocation)
        {
            logger.LogInformation($"参数:{Newtonsoft.Json.JsonConvert.SerializeObject(invocation.Arguments)}");
            logger.LogInformation($"{invocation.Method.Name}方法正在执行");
            invocation.Proceed();//执行call方法
            logger.LogInformation($"{invocation.Method.Name}方法执行完毕");
            logger.LogInformation($"返回值:{Newtonsoft.Json.JsonConvert.SerializeObject(invocation.ReturnValue)}");
        }
    }
}

还可以做缓存处理

public class CusotmInterceptor : IInterceptor//切入者逻辑
{
    private static Dictionary<string, object> cacheDictionary = new Dictionary<string, object>();//记录缓存
    public void Intercept(IInvocation invocation)
    {
        string cacheKey = invocation.Method.Name;//用方法名做钥匙,存在方法就不缓存,不存在就缓存数据。
        if (cacheDictionary.ContainsKey(cacheKey))//如果有缓存
        {
            invocation.ReturnValue = cacheDictionary[cacheKey];//返回缓存的结果。
        }
        else //无缓存
        {
            invocation.Proceed();//执行方法
            cacheDictionary[cacheKey] = invocation.ReturnValue;//记录缓存
        }
    }
}

IInterceptorSelector 扩展,不需要在去类和接口上标记 [Intercept(typeof(CusotmInterceptor))] 就可以做拦截

public class CusotmInterceptorSelector : IInterceptorSelector//IInterceptor的扩展:用来选择切入者逻辑的类简单执行哪一个
{
    public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
    {
        //return interceptors;//返回所有,类和接口上标记的都可以获取到。
        return new IInterceptor[] { new CusotmInterceptor() };//也可以选择哪一个切入者,多个在new。
    }
}

还需要ioc容器注册依赖关系

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => {
    containerBuilder.RegisterType<Microphone>().As<IMicrophone>()
    .EnableInterfaceInterceptors(new ProxyGenerationOptions(){ //指定使用的IInterceptor,不需要在去类和接口上标记:[Intercept(typeof(CusotmInterceptor))]
        Selector = new CusotmInterceptorSelector()//指定了选择者,交给这个类来执行我们需要拦截的切入者,指定了就会执行选择者,不需要在标记
    });
    containerBuilder.RegisterType<CusotmInterceptor>();//把aop扩展方法也注入进来才能使用,里面写方法前后执行动作。
});

 



posted @ 2022-12-18 05:37  Akai_啊凯  阅读(2609)  评论(0编辑  收藏  举报