其他分享
首页 > 其他分享> > 第三方DI框架:Autofac

第三方DI框架:Autofac

作者:互联网

什么情况下需要引入第三方容器组件?
实际上是大部分情况默认的容器组件是够我们使用的,但是当我们需要一些非常特殊的场景时,如:

.NET Core的依赖注入框架的核心扩展点是:IServiceProviderFactory
public interface IServiceProviderFactory<TContainerBuilder>
第三方的依赖注入容器都是使用了这个类来作为扩展点,把自己注入到我们整个的框架里面来,也就是说我们在使用这些依赖注入框架的时候,我们不需要关注说:谁家的特性、谁家的接口是什么样子的,我们只需要使用官方核心的定义就可以了,我们不需要直接依赖这些框架;

public interface IMyService{
    void ShowCode();
}

public class MyService: IMyService{
    public void ShowCode(){
        Console.WriteLine($"MyService.ShowCode:{GetHashCode()}");
    }
}

public class MyServiceV2: IMyService{
    public MyNameService NameService{get; set;}
    public void ShowCode(){
        Console.WriteLine($"MyServiceV2.ShowCode:{GetHashCode()}, NameService是否为空:{NameService == null}");
    }
}

public class MyNameService{}

引入Autofac包:

在 入口文件 中加入:
image
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) 的作用:注册第三方容器的入口

在 Startup.cs 中添加一个 ConfigureContainer方法:

public void ConfigureContainer(ContainerBuilder builder){
    // 一般的写法:先注册具体的实现,然后再告诉它我们想把它标记哪个服务的类型,与之前自带注入的写法是相反的
    builder.RegisterType<MyService>().As<IMyService>();

    // 命名注册:当我们需要把一个服务注册多次,并且用不同的命名来作为区分的时候,我们可以用这种方式
    builder.RegisterType<MyServiceV2>().Named<IMyService>("service2");
}

上述代码中的 命名注册,是如何去使用它的呢?在 Starup.cs 中:

public ILifetimeScope AutofacContainer{get; private set;}   // 这里是把跟容器注册进来

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    // Autofac容器获取实例的方式是一组Resolve的方法, ResolveNamed 和  ResolveKey 作用相同,用法不同而已;
    var service = this.AutofacContainer.ResolveNamed<IMyService>("service2");
    service.ShowCode();       // 这里输出的是 MyServiceV2

    var servicenamed = this.AutofacContainer.Resolve<IMyService>();
    servicenamed.ShowCode();       // 这里输出的是 MyService

    ...
}

运行后输出:
image

注意:Autofac容器获取实例的方式是一组Resolve的方法:
image

属性注入

只需要在后面加入 .PropertiesAutowired() 即可:

public void ConfigureContainer(ContainerBuilder builder)
{
    //// 一般的写法:先注册具体的实现,然后再告诉它我们想把它标记哪个服务的类型
    //builder.RegisterType<MyService>().As<IMyService>();

    //// 命名注册:当我们需要把一个服务注册多次,并且用不同的命名来作为区分的时候,我们可以用这种方式
    //builder.RegisterType<MyServiceV2>().Named<IMyService>("service2");

    builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired();
}
public ILifetimeScope AutofacContainer{get; private set;}   // 这里是把根容器注册进来

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    // Autofac容器获取实例的方式是一组Resolve的方法
    //var service = this.AutofacContainer.ResolveNamed<IMyService>("service2");
    //service.ShowCode();       // 这里输出的是 MyServiceV2

    var servicenamed = this.AutofacContainer.Resolve<IMyService>();
    servicenamed.ShowCode();       // 这里输出的是 MyService

    ...
}

image
NameService为空,说明 NameService 属性没有注入进来
我们需要将 MyNameService 也注入进容器中:

public void ConfigureContainer(ContainerBuilder builder)
{
    //// 一般的写法:先注册具体的实现,然后再告诉它我们想把它标记哪个服务的类型
    //builder.RegisterType<MyService>().As<IMyService>();

    //// 命名注册:当我们需要把一个服务注册多次,并且用不同的命名来作为区分的时候,我们可以用这种方式
    //builder.RegisterType<MyServiceV2>().Named<IMyService>("service2");

    builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired();
    builder.RegisterType<MyNameService>();
}

image
发现 MyNameService 已经不为空了,说明 NameService 已经有实例了,属性注入已经成功了

AOP

我们在不期望改变原有类的情况下,在方法执行时嵌入一些逻辑,让我们可以在方法执行的切面上任意的插入我们的逻辑

public class MyInterceptor: IInterceptor    // 类必须要继承至 IInterceptor,并且要实现 Intercept方法
{
    public void Intercept(IInvocation invocation)
    {
        System.Console.WriteLine($"Intercept before,Method:{invocation.Method.Name}");
        invocation.Proceed();
        System.Console.WriteLine($"Intercept after,Method:{invocation.Method.Name}");
    }
}
public void ConfigureContainer(ContainerBuilder builder)
{
    // 首先我们需要把我们的拦截器MyInterceptor注册到容器里面去
    builder.RegisterType<MyInterceptor>();
    // 然后我们把MyServiceV2注册进去,并且允许它属性注册,开启拦截器需要我们使用InterceptedBy这个方法,并且把我们的类型注册进去,最后还要执行一个开关让我们允许接口拦截器
	// 拦截器分两种类型,一种是接口类型的接口拦截器(常用)和类拦截器,当我们的服务的类型是接口的时候就需要用接口拦截器类型;而如果我们没有基于接口设计我们的类,而是去实现了类的话,我们就需要用类拦截器,类拦截器需要我们把方法设计为虚方法,这样子允许继承类重载的情况下,才可以拦截到我们的具体方法
    builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired().InterceptedBy(typeof(MyInterceptor)).EnableInterfaceInterceptors();
}
public ILifetimeScope AutofacContainer{get; private set;}   // 这里是把根容器注册进来

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

    var servicenamed = this.AutofacContainer.Resolve<IMyService>();
    servicenamed.ShowCode();       // 这里输出的是 MyService

    ...
}

image

子容器

我们可以创建Scope来作为子容器;
下面代码中,我们实际上是可以给子容器进行命名,Autofac具备给子容器进行命名的特性;

#region 子容器
这里我们可以把一个服务注入到子容器中,并且是特定的命名的子容器,那就意味着我们在其他的子容器里面是获取不到这个对象的;
builder.RegisterType<MyNameService>().InstancePerMatchingLifetimeScope("myscope");
#endregion
public ILifetimeScope AutofacContainer { get; private set; }   // 这里是把跟容器注册进来

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

    #region 子容器
    using(var myscope = AutofacContainer.BeginLifetimeScope("myscope"))
    {
        var service0 = myscope.Resolve<MyNameService>();
        using (var scope = myscope.BeginLifetimeScope())
        {
            var service1 = scope.Resolve<MyNameService>();
            var service2 = scope.Resolve<MyNameService>();

            Console.WriteLine($"service1=service2:{service1 == service2}");
            Console.WriteLine($"service1=service0:{service1 == service0}");
        }
    }
    #endregion

    ...
}

image

标签:容器,Autofac,DI,builder,AutofacContainer,第三方,注册,我们,public
来源: https://www.cnblogs.com/abc1069/p/16212015.html