内置 IOC 不支持属性注入?Autofac 到底怎么接管底层容器的?自己写一个 IOC 容器替换进去需要几步?
如果你也被这些问题困扰过,这篇文章带你从 ASP.NET Core 源码 出发,逐行拆解 IOC 容器的注册、实例化、替换全流程,最后手写一个简易 IOC 容器完成框架替换。
建议收藏,反复阅读。
01前情回顾:从启动到响应,IOC 始终在场
在之前的源码专题中,我们已经完成了1件大事:
- 启动 & 响应两大流程
拆解,启动流程分为 6 个环节,从 WebApplication.CreateBuilder到app.Run()的完整链路
在整个启动流程中,有一个角色贯穿始终——IOC 容器。
无论是框架内置服务的注册、开发者自定义服务的注入,还是第三方容器(如 Autofac)的接管,所有动作都围绕 IOC 展开。但我们在前两期只是"路过"了它,没有深入。今天,我们就来彻底拆解它。

02IOC 三层递进扩展:从注册替换到容器替换
ASP.NET Core 的扩展性,本质上就是基于 IOC 的无止境扩展。按照扩展的深度和层级,可以划分为三个层次,层层递进:

| 第一层 | |||
| 第二层 | |||
| 第三层 |
扩展1:IOC 注册替换 —— IControllerFactory
这是 ASP.NET Core 最常用的框架扩展点,几乎无处不在。
默认情况下,框架使用 DefaultControllerFactory 来创建控制器。我们可以自定义实现 IControllerFactory 接口,替换掉默认实现:



CreateController 里写日志、做权限校验、加缓存,任何你想要的事情。这种"注册替换"的扩展方式,在日常开发中用得特别特别多。IControllerFactory 也是一个选择,而且它更贴近控制器实例化这一层。扩展2:自定义控制器激活器 —— IControllerActivator
默认容器不支持属性注入,也不支持 AOP。但在 ASP.NET Core 范围内,我们可以通过自定义 IControllerActivator 来扩展这些能力。
先看自定义激活器的实现:




AddControllersAsServices() 的作用
这里有一个关键方法 AddControllersAsServices(),它的作用是把控制器类型注册到 IOC 容器中。

不加这个方法,控制器类型不会注册到容器中,GetService(controllerType) 会返回 null。加上之后,控制器本身也变成了 IOC 管理的服务,才能被容器正确实例化。
扩展2 的本质:这也是标准的 IOC 注册替换扩展,但在替换的同时添加了自定义动作——属性注入。甚至搞个简易 AOP 扩展也不难。但这是"小道",满足一些特殊需求。如果要更彻底的 AOP、更灵活的注册方式,需要进入第三层。

扩展3:完整替换底层 IOC 容器 —— Autofac
这是最常见的容器替换方案。很多人会用 Autofac,但很想懂 why——为什么要换?怎么换的?
Autofac 的优势
- AOP 扩展方便
原生支持 Castle DynamicProxy,方法拦截开箱即用 - 注册方式灵活
支持泛型注册、反射注册、实例注册、委托注册、程序集批量注册等 N 种方式 - Module 模块化
通过自定义 Module 类,将不同业务模块的注册逻辑解耦
实操步骤
第一步:Nuget 安装







03上帝视角:内置 IOC 完整生命周期
要理解 IOC 容器的全貌,需要先理解一个核心设计:注册和实例化是分开的。
IOC 容器的两件事被拆开设计
第一件事:注册 —— ServiceCollection,负责收集所有服务注册关系
第二件事:实例化 —— ServiceCollection.BuildServiceProvider(),生成真正的容器实例
生命周期四阶段
| 阶段1:初始化Builder | ||
| 阶段2:Build() 实例化 | ||
| 阶段3:容器替换 | ||
| 阶段4:请求分配Scope |

Program.cs 中写的 builder.Services.AddXxx(),并不是在那一刻就完成了注册的"最终生效"。它只是把注册关系保存到了 ServiceCollection(或者暂存为委托)。真正的注册执行,发生在 builder.Build() 阶段。04源码拆解:IOC 注册与实例化流程
各种 IOC 注册的本质
系统中的各种 IOC 注册,本质上都在做一件事:写入 ServiceCollection。
框架内置注册(如 AddMvcCore、AddRouting等)→ 写入 ServiceCollection开发者注册( builder.Services.AddTransient等)→ 写入 ServiceCollection如果在 Builder 初始化阶段还没有 ServiceCollection,就先写入委托,后面执行委托

三段逻辑逐行解读:
- 实例化 ServiceCollection
这是 IOC 注册的容器集合,所有的注册关系都存在这里 - 添加各种内置注册
框架自身需要的服务全部注册进来,包括默认的 DefaultServiceProviderFactory——这就是为什么你不替换时,用的是内置容器 - 执行开发者委托
你在 Program.cs中写的builder.Services.AddXxx(),本质是注册了委托,在这里统一执行。注意顺序——在默认注册之后。

05核心亮点:容器替换源码逐行拆解
这是本文的核心亮点。容器替换到底是怎么发生的?答案在 Build() 里面的 GetProviderFromFactory 方法。
GetProviderFromFactory 方法逐行解读


三段逻辑讲透"默认容器如何被第三方容器接管"
第一段:先用内置方式构建一个"临时"的默认容器实例。这个容器实例的主要作用不是给业务用,而是从中获取工厂。
第二段:从默认容器中获取 IServiceProviderFactory。这就是扩展点的核心!开发者在 Program.cs 中写的 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()),本质就是注册了一个不同的工厂。这个工厂会在这一步被取出来。
第三段:用取到的工厂创建 Builder 和 Provider。对于 Autofac 来说,CreateBuilder 会把 ServiceCollection 中的所有注册关系转换到 Autofac 的 ContainerBuilder 中,CreateServiceProvider 则生成 Autofac 的容器实例并包装成 IServiceProvider 返回。
所以替换了 Factory,就能替换掉容器实例。整个框架后续用的 Provider 就全部走第三方容器了。


06手写简易自定义 IOC 容器:四层结构完整设计
理解了容器替换的原理,我们来手写一个简易 IOC 容器,完成框架替换。需要准备四层组件:
| 自定义容器接口 | ||
| 自定义Factory | ||
| 自定义Builder | ||
| 自定义Provider |
第一层:自定义容器接口 IElevenContainer

三个方法:泛型注册、类型注册、实例获取。这是最简化的容器契约。
第二层:自定义 Factory —— YueContainerFactory
Factory 必须实现 IServiceProviderFactory<YueContainerBuilder> 接口,承担两个职责:
生成 Provider 把注册的映射关系转换到自实现容器

第三层:自定义 Builder —— YueContainerBuilder
Builder 真正负责完成注册关系转换 + 生成 Provider:

第四层:自定义 Provider —— YueServiceProvider
Provider 是最终提供实例获取的地方,里面包裹上自定义容器(或包裹内置容器做过渡):

替换接入框架
四层组件准备好后,只需一行代码完成替换:
// 替换为自己的容器工厂builder.Host.UseServiceProviderFactory(new YueContainerFactory());
四层组件完整调用链路

逻辑闭环:Factory 是入口 → 找 Builder → Builder 完成映射关系转换 + 生成 Provider → Provider 是包了一层的 Container。之后框架所有的 GetService 调用都会走到你的 Provider,你的 Provider 再调用你的容器完成实例获取。逻辑完全闭环。
本文的手写容器是简化版,ElevenContainer 的 Regist/Resolve 方法留了 NotImplementedException。完整的 IOC 容器需要支持生命周期管理、依赖解析、循环依赖检测等,复杂度很高。但这四层结构的设计思路是完整的,理解了这个骨架,再看 Autofac 源码就清晰多了。
07完整 IOC 知识点总结
IOC 容器核心知识点回顾
- 内置 IOC 容器的使用
—— AddTransient/AddScoped/AddSingleton 三种生命周期,构造函数注入,GetService/GetServices 获取实例 - IOC/DI 的理解
—— 控制反转是思想,依赖注入是实现。容器负责实例化和管理对象生命周期,开发者只声明依赖 - IOC 三层扩展
—— 扩展1: IControllerFactory 注册替换;扩展2: IControllerActivator 激活器自定义(属性注入/简易AOP);扩展3: IServiceProviderFactory 容器整体替换(Autofac) - 源码解读 IOC 初始化
—— BuildCommonServices 中先 new ServiceCollection,再添加内置注册,最后执行开发者委托。执行顺序:内置注册 → 开发者委托 - 容器替换核心
—— GetProviderFromFactory 方法三段逻辑:获取默认容器 → 获取工厂 → 用工厂创建新 Provider。替换 Factory 即替换容器 - 第三方容器接入
—— Autofac 通过实现 IServiceProviderFactory 接口,在 CreateBuilder 阶段转换注册关系,在 CreateServiceProvider 阶段生成新容器 - 手写简易 IOC 容器
—— 四层结构:IElevenContainer(容器接口)+ YueContainerFactory(工厂)+ YueContainerBuilder(转换器)+ YueServiceProvider(提供者)
HttpContext.RequestServices 作用域容器原理
最后点明一个关键原理:每次 HTTP 请求来了,框架会创建一个 Scope 级别的 ServiceProvider,保存到 base.HttpContext.RequestServices。
所有的实例获取都基于这个 Scope Provider——这就是 Scoped 生命周期的实现原理。同一个请求内,Scoped 服务是同一个实例;不同请求之间,Scoped 服务是不同实例。

作用域单例靠的是作用域的容器实例——每次请求响应时,容器会 CreateScope,然后一直用这个容器。跟多线程没关系,是跟请求绑定的。
你在项目中用的是内置 IOC 还是 Autofac?为什么选择它?
有没有尝试过自己实现一个简易 IOC 容器?遇到了什么坑?
欢迎在评论区留言讨论,点赞最高的评论将获得下期源码专题的优先预告!
夜雨聆风