【问题标题】:DI Container: inject the right components from a collection of same types?DI Container:从相同类型的集合中注入正确的组件?
【发布时间】:2026-01-24 22:45:01
【问题描述】:

我正在尝试基于构造函数注入在 PHP 中破解我自己的依赖注入容器。容器实例化复杂对象,并使用反射根据构造函数中的类型提示将所需的对象注入它们。

我显然偶然发现的一件事是,我可以注册多个可以注入的相同类型的组件(扩展相同的类/实现相同的接口)。例如,如果两个对象都需要实现 Iterator 接口的不同对象怎么办。 DI Containers 通常如何处理这个问题?如何让容器决定哪些接口不明确的对象需要注入哪些复杂对象?

或者是单个 DI 容器只负责创建一种类型的复杂对象?换句话说:为每个复杂对象实例化一个不同的 DI 容器。我很难想象这是意图,对吧?

【问题讨论】:

  • 我删除了标签“php”,因为实现语言的选择与问题无关。

标签: dependency-injection ioc-container ambiguity


【解决方案1】:

您所描述的不是 DI 本身,而是自动装配,比普通 DI 更进一步。通常使用 DI,您可以显式配置哪些组件连接到哪些组件。

Autowired DI 仅适用于粗粒度组件,其中给定类型(例如 DAO)只有一个合理的实现。在类型不明确的情况下,您可以将其中一个标记为“主要”,或者将其他标记为“非候选”,或者使用您需要的组件的名称显式标记依赖项。

如果有帮助,您可以阅读 Spring 如何处理自动装配 herehere

【讨论】:

  • 斯卡夫曼,感谢您的澄清。我不知道这被称为自动装配。但这听起来很明智。我想我应该选择你最后提出的选项,因为从长远来看,其他选项可能会导致不良影响。我正在努力使它成为一个防弹的。回到绘图板。 :)
  • 你可能是对的。但我通常想自己尝试一下这些东西(只是为了以后依靠现有的解决方案哈哈 ;-),以帮助我更好地理解它的含义。我也想提高我的 OOP 技能,并喜欢这样锻炼。 :) 感谢其他来源顺便说一句。第一个几乎给出了您给出的选项以及我从中得出的结论,因为如果存在无法解决的歧义,我需要抛出异常。
【解决方案2】:

根据您的问题的意思,以下是 Guice(Java 的 DI 框架)使用的内容:

如果您每次收到Foo 的注入请求时只需要一个不同的对象,那么在连接您的应用程序时,不要将Foo.class 绑定到Foo 的特定实例,而是绑定@987654324 @,这是一个按需创建Foo 的对象。然后,每个注入Foo 的地方都会得到一个新的实例。如果您只是将Foo 绑定到一个类而不是Foo 实例(例如,Foo 是一个接口,并且您将它绑定到RealFoo.class 这是一个实现Foo 的类),您还可以获得相同的效果 - 每次都会创建一个新实例。 (这是默认的“无作用域”行为。Guice 作用域的行为超出了本注释的范围)

但是,如果您需要在连接应用程序时构建两个 Foo 实例,然后能够说“这个 Foo 实例在创建 BarBaz 时被使用,而在制作Bumble" 时会使用另一个实例,您所做的是注释注入点,然后在连接您的应用程序时说:

Foo foo1 = new Foo("1");
Foo foo2 = new Foo("2");
bind(Foo.class).annotatedWith(Names.named("Bar")).toInstance(foo1);
bind(Foo.class).annotatedWith(Names.named("Baz")).toInstance(foo1);
bind(Foo.class).annotatedWith(Names.named("Bumble")).toInstance(foo2);

我假设您已使用以下内容注释 Bar 的构造函数的参数:

public Bar(@Named("Bar") Foo foo) { ...

BazBumble 也是如此。当然,如果你对BarBaz的构造函数进行了同样的注解,你可以跳过bind中的一行。

我知道有基于反射的 php 注释处理框架,或者您可以使用基于参数名称的约定。

【讨论】:

  • 哦,这是一个比我想象的更复杂的实现。我什至不担心用不同的注入实例化相同的对象(我还没有想到)。相反,我指的是不同的对象,它们都需要注入相同基类型或基接口的对象,但它们实际上是不同的类。 (请原谅我在这里混淆了正确的术语)。但是你的例子把事情放在了一个完全不同的角度。好点。 :)
  • 顺便说一句,我不确定我是否喜欢注释选项。我希望尽可能保持“干净”,而不必更改主题实现。如果您知道我的意思,它应该能够与任何物体一起使用架子。构造函数参数名称,或者更好的是,在 DI 的配置中显式命名依赖项可能是可行的方法。