跳到内容

延迟服务

编辑此页

另请参阅

其他延迟注入服务的方式是通过服务闭包服务订阅器

为什么使用延迟服务?

在某些情况下,您可能想要注入一个实例化开销较大的服务,但该服务并非总是在您的对象内部使用。例如,假设您有一个 NewsletterManager,并且您将一个 mailer 服务注入到其中。您的 NewsletterManager 上只有少数方法实际使用 mailer,但即使您不需要它,也会始终实例化一个 mailer 服务,以便构建您的 NewsletterManager

配置延迟服务是解决此问题的一种方法。对于延迟服务,实际上注入的是 mailer 服务的“代理”。它看起来和行为都像 mailer,只是在您以某种方式与代理交互之前,实际上不会实例化 mailer

警告

延迟服务不支持 finalreadonly 类,但您可以使用接口代理来解决此限制。

在 PHP 8.0 之前的版本中,延迟服务不支持内置 PHP 类(例如 PDO)的带默认值的参数。

配置

您可以通过操作服务的定义将服务标记为 lazy

1
2
3
4
# config/services.yaml
services:
    App\Twig\AppExtension:
        lazy: true

一旦您将服务注入到另一个服务中,应该注入一个具有与表示该服务的类相同签名的延迟幽灵对象。延迟幽灵对象是一个创建时为空的对象,并且能够在第一次被访问时自行初始化的对象。直接调用 Container::get() 时也会发生同样的情况。

要检查您的延迟服务是否正常工作,您可以检查接收到的对象的接口

1
2
dump(class_implements($service));
// the output should include "Symfony\Component\VarExporter\LazyObjectInterface"

您还可以通过 Autoconfigure 属性来配置服务的延迟性。例如,要将您的服务定义为延迟加载,请使用以下方法

1
2
3
4
5
6
7
8
9
10
namespace App\Twig;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Twig\Extension\ExtensionInterface;

#[Autoconfigure(lazy: true)]
class AppExtension implements ExtensionInterface
{
    // ...
}

当您的服务通过 Autowire 属性注入时,您也可以配置延迟加载

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace App\Service;

use App\Twig\AppExtension;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MessageGenerator
{
    public function __construct(
        #[Autowire(service: 'app.twig.app_extension', lazy: true)] ExtensionInterface $extension
    ) {
        // ...
    }
}

此属性还允许您在使用延迟加载时定义要代理的接口,并支持联合类型的延迟自动装配

1
2
3
4
5
public function __construct(
    #[Autowire(service: 'foo', lazy: FooInterface::class)]
    FooInterface|BarInterface $foo,
) {
}

另一种可能性是使用 Lazy 属性

1
2
3
4
5
6
7
8
9
10
namespace App\Twig;

use Symfony\Component\DependencyInjection\Attribute\Lazy;
use Twig\Extension\ExtensionInterface;

#[Lazy]
class AppExtension implements ExtensionInterface
{
    // ...
}

此属性可以应用于应该延迟加载的类和参数。它定义了一个可选参数,用于定义代理和交叉类型的接口

1
2
3
4
5
public function __construct(
    #[Lazy(FooInterface::class)]
    FooInterface|BarInterface $foo,
) {
}

7.1

#[Lazy] 属性在 Symfony 7.1 中引入。

接口代理

在底层,为延迟加载服务生成的代理继承自服务使用的类。但是,有时这根本不可能(例如,因为该类是 final 且无法扩展)或不方便。

为了解决此限制,您可以配置代理仅实现特定的接口。

1
2
3
4
5
6
7
8
# config/services.yaml
services:
    App\Twig\AppExtension:
        lazy: 'Twig\Extension\ExtensionInterface'
        # or a complete definition:
        lazy: true
        tags:
            - { name: 'proxy', interface: 'Twig\Extension\ExtensionInterface' }

就像配置部分一样,您可以使用 Autoconfigure 属性,通过传递其 FQCN 作为 lazy 参数值来配置要代理的接口

1
2
3
4
5
6
7
8
9
10
namespace App\Twig;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Twig\Extension\ExtensionInterface;

#[Autoconfigure(lazy: ExtensionInterface::class)]
class AppExtension implements ExtensionInterface
{
    // ...
}

注入到其他服务中的虚拟代理将仅实现指定的接口,而不会扩展原始服务类,从而允许使用 final 类延迟加载服务。您可以通过添加新的“proxy”标签来配置代理以实现多个接口。

提示

此功能还可以充当安全保障:鉴于代理不扩展原始类,因此只能调用接口定义的方法,从而防止调用特定于实现的方法。如果您类型提示了具体实现而不是接口,它还可以防止注入依赖项。

本作品,包括代码示例,根据Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本