跳到內容

延遲服務

編輯此頁

參見

其他延遲注入服務的方式是透過服務閉包服務訂閱器

為何使用延遲服務?

在某些情況下,您可能想要注入一個實例化成本較高,但在物件內部不一定總是使用的服務。例如,假設您有一個 NewsletterManager,並且您將一個 mailer 服務注入其中。您的 NewsletterManager 上只有少數方法實際使用 mailer,但即使您不需要它,為了建構您的 NewsletterManagermailer 服務始終會被實例化。

設定延遲服務是這個問題的一個解答。使用延遲服務,實際上注入的是 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 授權條款授權。
目錄
    版本