注射类型

注射类型

使阶级的依赖关系明确并要求它们被注入它是一种很好的方法,使课程更加可重复使用,可测试和与他人分离。

有几种方式可以注入依赖项。每个注入点都有需要考虑的优点和缺点,以及使用服务容器时使用它们的不同方式。

构造函数注射

注入依赖项的最常见方法是通过类的构造函数。要做到这一点,你需要向构造函数签名添加一个参数来接受依赖关系:

/ / src /邮件/欧宝app在哪里找 NewsletterManager.php名称空间App \邮件;// ......班级欧宝app在哪里找NewsletterManager{私人的$邮箱;公共函数__构造MailerInterface$邮箱{$这一点->邮箱=$邮箱;}// ......}

你可以在服务容器配置中指定你想要注入的服务:

  • yaml.
    1 2 3 4 5 6
    #配置/ services.yaml服务#...App \ Mai欧宝app在哪里找l \ NewsletterManager参数['@mailer']
  • XML
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    < !--config/services.xml -->< ?encoding="UTF-8"<容器xmlns =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”XMLNS:XSI =“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services.https://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services/services-1.0.xsd“><服务><! -  ...  - ><服务ID =“app \ mai欧宝app在哪里找l \ newslettermanager”><参数类型=“服务”ID =“邮件”/>< /服务>> < /容器
  • PHP
    1 2 3 4 5 6 7 8 9 10 11 12
    // config / services.php名称空间欧宝娱乐app下载地址Symfony \ Component \ DependencyIngreation \ Loader \ Configurator;使用App \ Mai欧宝app在哪里找l \ NewsletterManager;返回函数containerconfigurator美元配置器{美元服务=美元配置器->服务();美元服务->欧宝app在哪里找NewsletterManager::班级//在Symfony 5.1之前的版本中,servi欧宝娱乐app下载地址ce()函数被称为ref()->args.服务'邮件'));};

小费

暗示注入的对象意味着您可以确定注入了合适的依赖项。通过类型暗示,如果注入不合适的依赖性,您将立即获得清晰的错误。通过类型暗示使用接口而不是类,您可以选择更灵活的依赖性。并假设您只使用界面中定义的方法,您可以获得该灵活性并仍然安全使用该对象。

使用构造仪注射有几个优点:

  • 如果依赖项是一个要求,并且在没有它的情况下,如果没有它,则通过构造函数注入它的情况下无法注入它。如果在没有它的情况下无法构建类,则会确保它存在。
  • 构造函数仅在创建对象时才会调用一次,因此您可以确定在对象的生命周期内不会更改依赖项。

这些优点确实意味着构造函数注入不适合处理可选依赖项。与类层次结构结合使用也更加困难:如果一个类使用构造函数注入,那么扩展它并覆盖构造函数就会出现问题。

Immutable-setter注入

另一种可能的注射是使用通过克隆原始服务来返回单独实例的方法,这种方法允许您使服务不变:

/ / src /邮件/欧宝app在哪里找 NewsletterManager.php名称空间App \邮件;// ......使用欧宝娱乐app下载地址symfony \ component \ mailer \ mailerInterface;班级欧宝app在哪里找NewsletterManager{私人的$邮箱;/ * ** @必需的* @return静态* /公共函数withMailerMailerInterface$邮箱自我{$新=克隆$这一点;$新->邮箱=$邮箱;返回$新;}// ......}

为了使用这种类型的注射,不要忘记配置它:

  • yaml.
    1 2 3 4 5 6 7 8
    #配置/ services.yaml服务#...app.欧宝app在哪里找newsletter_manager班级App \ Mai欧宝app在哪里找l \ NewsletterManager呼叫-withMailer!returns_clone.['@mailer']
  • XML
    12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    < !--config/services.xml -->< ?encoding="UTF-8"<容器xmlns =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”XMLNS:XSI =“https://www.w3.org/2001/xmlschema-instance”xsi: schemaLocation =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services.https://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services/services-1.0.xsd“><服务><! -  ...  - ><服务ID =“app.欧宝app在哪里找newsletter_manager”类=“app \ mai欧宝app在哪里找l \ newslettermanager”><电话方法=“withMailer”returns-clone =“真正的”><参数类型=“服务”ID =“邮件”/>< / >电话< /服务>> < /容器
  • PHP
    1 2 3 4 5 6 7
    // config / services.php使用App \ Mai欧宝app在哪里找l \ NewsletterManager;使用欧宝娱乐app下载地址Symfony \ DependencyInjection \ \组件参考;// ......$容器->登记“app.欧宝app在哪里找newsletter_manager”欧宝app在哪里找NewsletterManager::班级->addmethodcall.'andmailer'[参考'邮件')),真正的);

笔记

如果您决定使用Autowiring,这种类型的注射需要添加一个@返回静止的DocBlock以便容器能够注册该方法。

如果您需要根据您的需求配置您的服务,因此此方法很有用,因此这是Imputable-Setter的优势:

  • 不可变定者与可选的依赖关系一起工作,这种方式,如果您不需要依赖项,则不需要调用Setter。
  • 与构造函数注入一样,使用不可变setter强制依赖关系在服务的生命周期内保持不变。
  • 这种类型的注入可以很好地与特征一起工作,因为服务可以组合在一起,通过这种方式,使服务更容易适应您的应用程序需求。
  • 可以多次调用setter,通过这种方式,向集合添加依赖项会变得更容易,并允许您添加数量可变的依赖项。

缺点是:

  • 由于setter调用是可选的,所以在调用服务的方法时,依赖项可以为空。在使用依赖项之前,必须检查该依赖项是否可用。
  • 除非服务被声明懒惰,否则它与在所谓的循环循环中相互引用的服务不兼容。

Setter注射

进入类的另一个可能的注入点是通过添加接受依赖项的Setter方法:

/ / src /邮件/欧宝app在哪里找 NewsletterManager.php名称空间App \邮件;// ......班级欧宝app在哪里找NewsletterManager{私人的$邮箱;/ * ** @必需的* /公共函数setmailer.MailerInterface$邮箱无效{$这一点->邮箱=$邮箱;}// ......}
  • yaml.
    1 2 3 4 5 6 7 8
    #配置/ services.yaml服务#...app.欧宝app在哪里找newsletter_manager班级App \ Mai欧宝app在哪里找l \ NewsletterManager呼叫-setmailer.['@mailer']
  • XML
    12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    < !--config/services.xml -->< ?encoding="UTF-8"<容器xmlns =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”XMLNS:XSI =“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services.https://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services/services-1.0.xsd“><服务><! -  ...  - ><服务ID =“app.欧宝app在哪里找newsletter_manager”类=“app \ mai欧宝app在哪里找l \ newslettermanager”><电话方法=“setMailer”><参数类型=“服务”ID =“邮件”/>< / >电话< /服务>> < /容器
  • PHP
    1 2 3 4 5 6 7 8 9 10 11
    // config / services.php名称空间欧宝娱乐app下载地址Symfony \ Component \ DependencyIngreation \ Loader \ Configurator;使用App \ Mai欧宝app在哪里找l \ NewsletterManager;返回函数containerconfigurator美元配置器{美元服务=美元配置器->服务();美元服务->欧宝app在哪里找NewsletterManager::班级->称呼“setMailer”[服务'邮件')));};

这次优点是:

  • Setter注入与可选依赖项一起工作得很好。如果不需要依赖项,就不要调用setter。
  • 您可以多次调用setter。如果该方法将依赖项添加到集合中,这将特别有用。然后,您可以拥有数量可变的依赖项。
  • 就像不可变设置器一样,这种类型的注入可以很好地处理trait,并允许你组合你的服务。

setter注入的缺点是:

  • 设置器可以称为多次,也可以长时间调用初始化后长,因此您无法确保在对象的生命周期内未替换依赖项(除了明确地编写Setter方法以检查它是否已被调用)。
  • 您不能确定setter将被调用,因此您需要添加检查,以确保注入任何所需的依赖项。

属性注入

另一种可能性是直接设置类的公共字段:

// ......班级欧宝app在哪里找NewsletterManager{公共$邮箱;// ......}
  • yaml.
    1 2 3 4 5 6 7 8
    #配置/ services.yaml服务#...app.欧宝app在哪里找newsletter_manager班级App \ Mai欧宝app在哪里找l \ NewsletterManager属性邮箱'@mailer'
  • XML
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    < !--config/services.xml -->< ?encoding="UTF-8"<容器xmlns =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”XMLNS:XSI =“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation =“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services.https://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services/services-1.0.xsd“><服务><! -  ...  - ><服务ID =“app.欧宝app在哪里找newsletter_manager”类=“app \ mai欧宝app在哪里找l \ newslettermanager”>名称=“邮件”类型=“服务”ID =“邮件”/>< /服务>> < /容器
  • PHP
    1 2 3 4 5 6 7 8 9 10 11
    // config / services.php名称空间欧宝娱乐app下载地址Symfony \ Component \ DependencyIngreation \ Loader \ Configurator;使用App \ Mai欧宝app在哪里找l \ NewsletterManager;返回函数containerconfigurator美元配置器{美元服务=美元配置器->服务();美元服务->“app.欧宝app在哪里找newsletter_manager”欧宝app在哪里找NewsletterManager::班级->财产'邮件'服务'邮件'));};

使用属性注入主要只有缺点,它类似于setter注入,但有这些附加的重要问题:

  • 您无法控制依赖项何时设置依赖项,可以在对象生命周期中的任何点更改它。
  • 您无法使用类型暗示,因此您无法确定在使用之前将在类代码中显式测试类实例之外注入哪些依赖项。

但是,知道这可以用服务容器来完成是很有用的,特别是当您正在处理超出您控制范围的代码时,例如在第三方库中,该库对其依赖项使用公共属性。

这项工作,包括代码样本,是在一个创作共用BY-SA 3.0执照。