延迟服务
为什么使用延迟服务?
在某些情况下,您可能想要注入一个实例化开销较大的服务,但该服务并非总是在您的对象内部使用。例如,假设您有一个 NewsletterManager
,并且您将一个 mailer
服务注入到其中。您的 NewsletterManager
上只有少数方法实际使用 mailer
,但即使您不需要它,也会始终实例化一个 mailer
服务,以便构建您的 NewsletterManager
。
配置延迟服务是解决此问题的一种方法。对于延迟服务,实际上注入的是 mailer
服务的“代理”。它看起来和行为都像 mailer
,只是在您以某种方式与代理交互之前,实际上不会实例化 mailer
。
配置
您可以通过操作服务的定义将服务标记为 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”标签来配置代理以实现多个接口。
提示
此功能还可以充当安全保障:鉴于代理不扩展原始类,因此只能调用接口定义的方法,从而防止调用特定于实现的方法。如果您类型提示了具体实现而不是接口,它还可以防止注入依赖项。