控制反转——Autofac框架
一、 组件注册方式
1. RegisterType(注册类型):“需要时请new一个”
builder.RegisterType<ExposureInfoViewModel>().AsSelf().SingleInstance();
- 作用:用于将一个具体的类注册到容器中,告知容器“当需要某个服务时,可以创建这个类的实例来提供它”
- 原理:告诉容器一个实现类型,当有组件请求该类型(或其实现的接口/基类)时,容器会new一个新的实例(根据生命周期配置,可能是新的,也可能是共享的)
- 使用:配合As<>来指定其暴露的服务(接口)。
2. RegisterInstance(注册实例):“给你这个现成的,一直用它”
var mySingleton = new MySingletonClass(); builder.RegisterInstance(mySingleton).As<ISingletonService>();
- 作用:用于将一个已经存在的、特定的对象实例注册到容器中。告诉容器:“不要自己创建新的,每次有人请求这个服务时,都返回 我给你的 这个唯一的、现成的实例”。
- 原理:你将一个已经实例化好的对象交给容器。容器不会尝试创建它、也不会管理它的销毁。每次解析该服务,返回的都是同一个对象引用。
- 特点:提供现有对象,容器不负责创建,只负责提供引用。
-
场景:
- 注册已经在程序启动时创建好的单例对象。
- 注册一些在容器之外创建和配置的对象(如第三方框架的入口点)。
- 注册常量值或配置对象。
- 你想完全控制某个对象的创建和初始化过程,然后将其提供给 DI 容器使用。
3. RegisterModule(注册模块):“把这些相关的注册打包一起执行”
public class DataAccessModule : Module { public string ConnectionString { get; set; } protected override void Load(ContainerBuilder builder) { // 在这个模块内部,你可以像在外部一样使用 builder 进行注册 builder.RegisterType<SqlUserRepository>() .As<IUserRepository>() .WithParameter(“connectionString”, ConnectionString) // 可以使用模块的属性来配置 .InstancePerLifetimeScope(); builder.RegisterType<SqlOrderRepository>() .As<IOrderRepository>() .WithParameter(“connectionString”, ConnectionString) .InstancePerLifetimeScope(); // 也可以注册其他模块 // builder.RegisterModule(new LoggingModule()); } }
//写法一:需要手动创建模块实例、可以在注册前对模块进行配置、适用于需要传递参数的模块 builder.RegisterModule(new DataAccessModule { ConnectionString = “MyConnString” }); //写法二:Autofac 会自动实例化模块、代码更简洁、要求模块有无参构造函数 builder.RegisterModule<DataAccessModule >()
- 作用:它不是直接注册服务,而是注册一个“模块”。模块是一个包含了一组相关注册的的逻辑单元。它是一种组织和管理注册代码的方式,用于实现关注点分离和模块化设计。
- 原理:创建一个继承自Autofac.Module的类,在其Load方法中编写一系列的注册语句。然后通过RegisterModule将这个类加入到容器构建器中,类内的所有注册都会被执行。
- 特点:
- 组织代码:将属于同一功能块(如数据访问、日志、Web API 控制器)的注册封装在一起,让
Global.asax或Program.cs中的代码更简洁。 - 可配置性:模块可以有自己的属性,允许在注册时进行配置(如上面的连接字符串)。
- 可重用性:封装好的模块可以在不同的项目中重用。
- 条件注册:可以在模块的
Load方法中编写逻辑,实现有条件的动态注册。
- 组织代码:将属于同一功能块(如数据访问、日志、Web API 控制器)的注册封装在一起,让
二、 服务暴露方式
1. AsSelf()
- 注册方式:将类本身作为服务注册,即类直接对应注册的服务。
- 适用场景:适用于类未继承任何接口的情况,或需要明确指定服务类型为类本身。
builder.RegisterType<ExposureInfoViewModel>().AsSelf().SingleInstance();
- builder.RegisterType<ExposureInfoViewModel>():告诉 Autofac 容器注册 ExposureInfoViewModel 类型。这样,当你需要 ExposureInfoViewModel 的实例时,Autofac 能自动创建并注入它。
- .AsSelf():指定注册时以自身类型(ExposureInfoViewModel)来解析。也就是说,只有请求 ExposureInfoViewModel 时才会返回这个实例,而不是以接口或基类的方式。
- .SingleInstance():指定该类型在整个容器生命周期内只创建一个实例(单例模式)。所有请求都会获得同一个 ExposureInfoViewModel 实例。
2. As<接口>()
- 注册方式:将类注册为指定接口的实现,即通过接口暴露服务。
- 适用场景:当类继承了接口时使用,可实现接口与类的关联,便于后续通过接口统一调用实现类。
builder.RegisterType<McsfLoggerFactory>().As<ILoggerFactory>().SingleInstance();
- .As<ILoggerFactory>():注册时,声明当请求 ILoggerFactory 接口时,实际提供 McsfLoggerFactory 的实例。也就是说,依赖注入时如果有类需要 ILoggerFactory,Autofac 会注入 McsfLoggerFactory。
3. 联合使用
builder.RegisterType<MultiService>().As<IServiceA>().As<IServiceB>().SingleInstance();
- .As<IServiceA>().As<IServiceB>():允许单个实现类同时作为多个接口的服务提供者,解析IServiceA或IServiceB时返回同一对象实例
- 典型场景:
- 日志服务同时实现IFileLogger和IConsoleLogger
- 数据访问层同时支持IRepository和IQueryProvider接口
builder.RegisterType<MyService>().As<IMyService>().AsSelf(); - .As<IMyService>().AsSelf():既可通过IMyService解析,也可直接解析MyService类型 - 典型场景: - 需要直接访问实现类特有方法(非接口声明的方法) - 单元测试中需Mock具体类而非接口
三、 生命周期管理方式
1. SingleInstance(单例模式)
- 在整个容器生命周期内只会创建一个实例,所有解析请求都返回同一个对象
- 适用于无状态服务或全局共享资源(如配置类、日志服务)
- 线程安全,适合多线程环境下的共享访问
using Autofac; using System; public interface IService { Guid Id { get; } } public class MyService : IService { public Guid Id { get; } = Guid.NewGuid(); } class Program { static void Main() { var builder = new ContainerBuilder(); // 关键注册:配置单例生命周期 builder.RegisterType<MyService>().As<IService>().SingleInstance(); using (var container = builder.Build()) { // 测试单例行为 using (var scope1 = container.BeginLifetimeScope()) { var service1 = scope1.Resolve<IService>(); var service2 = scope1.Resolve<IService>(); Console.WriteLine($"同作用域实例比对: {service1.Id == service2.Id}"); // True } // 新建作用域测试 using (var scope2 = container.BeginLifetimeScope()) { var service3 = scope2.Resolve<IService>(); var service4 = scope2.Resolve<IService>(); Console.WriteLine($"跨作用域实例比对: {service3.Id == service4.Id}"); // True } } } }
2. InstancePerDependency(瞬时模式)
- 每次解析请求都会创建一个新实例,是Autofac的默认行为
- 适用于需要独立状态的短期对象(如请求处理器、临时计算服务)
- 可能增加内存开销,频繁创建/销毁对象时需评估性能
using Autofac; using System; public interface ILogger { void Log(string message); } public class ConsoleLogger : ILogger { private Guid _id = Guid.NewGuid(); public void Log(string message) => Console.WriteLine($"[实例ID:{_id}] {message}"); } class Program { static void Main() { var builder = new ContainerBuilder(); // 关键注册:配置瞬时生命周期(默认行为,显式声明更清晰) builder.RegisterType<ConsoleLogger>().As<ILogger>().InstancePerDependency(); using (var container = builder.Build()) { // 同一作用域内解析两次 using (var scope = container.BeginLifetimeScope()) { var logger1 = scope.Resolve<ILogger>();//每次调用Resolve()都会生成全新实例,通过Guid验证实例唯一性 var logger2 = scope.Resolve<ILogger>(); logger1.Log("第一次调用"); // 输出不同实例ID logger2.Log("第二次调用"); } } } }
???builder.RegisterInstance(ConsoleLogger).As<ILogger>().InstancePerDependency(); 每次请求得到的是同一个实例吗?
答:RegisterInstance和InstancePerDependency本身是矛盾且无效的,最终的行为将由容器决定,但通常会产生非预期的结果。
3. InstancePerLifetimeScope(作用域模式)
- 在同一生命周期作用域内返回同一实例,不同作用域创建不同实例
- 典型应用场景包括HTTP请求周期(如ASP.NET中每个请求一个作用域)
- 需显式调用
BeginLifetimeScope()创建作用域,避免内存泄漏

using Autofac; using System; public interface IService { void Execute(); } public class MyService : IService { public void Execute() => Console.WriteLine($"实例ID: {this.GetHashCode()}"); } class Program { static void Main() { var builder = new ContainerBuilder(); // 关键注册:配置作用域生命周期 builder.RegisterType<MyService>().As<IService>().InstancePerLifetimeScope(); using (var container = builder.Build()) { // 场景1:同一作用域内实例共享 using (var scope1 = container.BeginLifetimeScope()) { var s1 = scope1.Resolve<IService>(); var s2 = scope1.Resolve<IService>(); s1.Execute(); // 输出相同哈希值 s2.Execute(); } // 场景2:不同作用域创建新实例 using (var scope2 = container.BeginLifetimeScope()) { var s3 = scope2.Resolve<IService>(); s3.Execute(); // 输出与scope1不同的哈希值 } } } }
浙公网安备 33010602011771号