如何令服务的参数/引用成为可选

3.4 版本
维护中的版本

有时,你的某个服务会有一个可选的依赖,这意味着该服务没有这个依赖时也能正常工作。前面例子中,app.mailer服务是必须要存在的,否则会抛出异常。通过修改newsletter_manager的服务定义,你可以令它对app.mailer的引用变为可选。容器将在依赖存在时实施注入,否则什么也不做:

将错失的依赖设为null 

你可以使用null技巧,来显式指定服务定义中的参数为null,如果该服务不存在的话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="app.mailer">
        <!-- ... -->
        </service>
 
        <service id="app.newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <argument type="service" id="app.mailer" on-invalid="null" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface;
 
$container->setDefinition('app.mailer', ...);
 
$container->setDefinition('app.newsletter_manager', new Definition(
    'AppBundle\Newsletter\NewsletterManager',
    array(
        new Reference(
            'app.mailer',
            ContainerInterface::NULL_ON_INVALID_REFERENCE
        )
    )
));

null配置技巧现已不被YAML驱动所支持

忽略错失的依赖 

忽略错失依赖,在行为上与显式指定“null”是一样的,除了在方法调用中使用时,方法本身会被删除。

下述例子中,容器将以构造器方式注入一个服务到app.newsletter_manager服务中,前提是该服务存在。若不存在,将移除构造方法:

1
2
3
4
5
# app/config/services.yml
services:
    app.newsletter_manager:
        class:     AppBundle\Newsletter\NewsletterManager
        arguments: ['@?app.mailer']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="app.mailer">
        <!-- ... -->
        </service>
 
        <service id="app.newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <call method="setMailer">
                <argument type="service" id="my_mailer" on-invalid="ignore"/>
            </call>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface;
 
$container->setDefinition('app.mailer', ...);
 
$container->setDefinition('app.newsletter_manager', new Definition(
    'AppBundle\Newsletter\NewsletterManager'
))->addMethodCall('setMailer', array(
    new Reference(
        'my_mailer',
        ContainerInterface::IGNORE_ON_INVALID_REFERENCE
    ),
));

YAML中特殊的@? 语法告诉服务容器这个依赖是可选的。当然, NewsletterManager类必须也要重新修改为可选依赖:

1
2
3
4
public function __construct(Mailer $mailer = null)
{
    // ...
}

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)