从零实现一个迷你 IoC 容器
一百行代码理解依赖注入的三个核心抽象:注册、解析与生命周期。
为什么要自己写一个
框架用久了会忘掉底层在做什么。用一个周末写个迷你容器,把「注册」「解析」「生命周期」三件事各摸一遍,比读十篇源码解析都管用。
核心数据结构
容器本质上是一个字典:服务类型到「怎么造出来」的映射。造法分三种:给实例、给工厂、给实现类型。
private readonly Dictionary<Type, Func<IServiceProvider, object>> _registrations = new();
注册
AddSingleton 存实例;AddScoped 与 AddTransient 存工厂。三者的差别只在「每次解析要不要新建」。
解析
拿到注册项,构造实例。如果构造函数还有依赖,就递归解析——这就是「依赖图」的遍历。要注意循环依赖:A 等 B,B 又等 A,递归会栈溢出,成熟容器的做法是检测中的标记加报错。
生命周期
Singleton 实例存容器里,全局一份;Scoped 存在「作用域」对象里,每个请求一份;Transient 每次新建,谁也不存。迷你版可以把作用域做成一个子容器,解析时先查自己的注册表,找不到再问父容器。
踩到的坑
第一版忘了处理「解析未注册类型」,直接空引用。第二版把 Scoped 服务注册成了 Singleton,结果请求级的 DbContext 被全局复用,数据串了。生命周期不是性能问题,是正确性问题。
收获
写完再回头看 ASP.NET Core 的容器,那些 IServiceProvider、IServiceScope 的抽象突然都有了具体形状。框架是翅膀,但知道翅膀怎么扇的,掉下来时才不会慌。
评论 0
还没有评论,来说两句吧。