延遲服務
為何使用延遲服務?
在某些情況下,您可能想要注入一個實例化成本較高,但在物件內部不一定總是使用的服務。例如,假設您有一個 NewsletterManager
,並且您將一個 mailer
服務注入其中。您的 NewsletterManager
上只有少數方法實際使用 mailer
,但即使您不需要它,為了建構您的 NewsletterManager
,mailer
服務始終會被實例化。
設定延遲服務是這個問題的一個解答。使用延遲服務,實際上注入的是 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" 標籤來設定代理實作多個介面。
提示
此功能也可以作為安全措施:假設代理不擴展原始類別,則只能呼叫介面定義的方法,從而防止呼叫實作特定的方法。如果您對具體實作進行了類型提示而不是介面,它還可以防止完全注入依賴項。