文件

§將外掛遷移至模組

注意:已於 2.5.x 中移除已棄用的 play.Plugin 系統。

如果您已實作 Play 外掛,請考慮將您的實作遷移為使用 play.api.inject.Module,而非已棄用的 Java play.Plugin 或 Scala play.api.Plugin 類型。

舊的 Plugin API 與使用 Module 的新 API 之間的主要差異在於,後者將完全採用依賴注入 (DI) - 您可以 在此 瞭解 Play 為何會對 DI 形成意見。

§原理

使用舊的 Plugin API 時,您需要提供一個包含數字和要載入的插件的完全限定名稱的 play.plugins 檔案。Play 會使用該數字來決定插件之間的總順序。這種方法很脆弱,因為很難確保數字對應到初始化順序中的正確位置,因此插件的相依項會在插件初始化之前初始化。此外,沒有辦法避免兩個插件使用相同的數字。這兩個問題都已透過使用 DI 解決。元件現在會明確地將其相依項宣告為建構函數參數,並在建構函數中執行初始化。

§建立 Module 類別

從繼承自 play.api.inject.Module 的類別開始,並提供 bindings 方法的實作。在此方法中,您應該將類型連接到具體實作,以便您的模組提供的元件可以在使用者的程式碼或其他模組中注入。以下是範例。

在 Java 中

import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;

import scala.collection.Seq;

public class MyModule extends Module {
  public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
    return seq(
      bind(MyComponent.class).to(MyComponentImpl.class)
    );
  }
}

在 Scala 中

import play.api.Configuration
import play.api.Environment

class MyModule extends Module {
  def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = {
    Seq(
      bind[MyComponent].to[MyComponentImpl]
    )
  }
}

請注意,如果您正在定義的元件需要另一個元件,您應該只將所需的元件新增為建構函數的相依項,並在建構函數前加上 @javax.inject.Inject 註解。DI 架構會處理其餘的工作。

注意:如果元件 B 需要 A,則 B 僅會在 A 初始化後初始化。

以下是需要 ApplicationLifecycle 元件的 MyComponentImpl 元件範例。

在 Java 中

import javax.inject.Inject;
import play.inject.ApplicationLifecycle;
import play.libs.F;

public interface MyComponent {}

class MyComponentImpl implements MyComponent {
  @Inject
  public MyComponentImpl(ApplicationLifecycle lifecycle) {
    // previous contents of Plugin.onStart
    lifecycle.addStopHook( () -> {
      // previous contents of Plugin.onStop
      return F.Promise.pure(null);
    });
  }
}

在 Scala 中

import javax.inject.Inject

import scala.concurrent.Future

import play.api.inject.ApplicationLifecycle

trait MyComponent

class MyComponentImpl @Inject() (lifecycle: ApplicationLifecycle) extends MyComponent {
  // previous contents of Plugin.onStart
  lifecycle.addStopHook { () =>
    // previous contents of Plugin.onStop
    Future.successful(())
  }
}

§連接

現在是時候將您新建立的 Module 類別新增至已啟用模組的集合中。執行此操作就像在設定檔中新增下列列一樣簡單

play.modules.enabled  += "my.module.MyModule"

如果您正在開發其他專案(包括子專案)將會使用的函式庫,請在 reference.conf 檔案中新增上述列(如果您還沒有 reference.conf,請建立一個並將其放置在 src/main/resources 中)。否則,如果是在結束 Play 專案中,它應在 application.conf 中。
如果是結束 Play 專案,您也可以建立一個名為 Module 的類別,並將其放入根目錄(「app」目錄)。

注意:如果您正在開發函式庫,強烈建議不要使用 play.modules.disabled 來停用模組,因為當應用程式載入模組時,這可能會導致非決定性結果(請參閱 此問題,了解為什麼您不應觸碰 play.modules.disabled)。事實上,play.modules.disabled 是供最終使用者覆寫預設啟用的模組。

§編譯時期 DI

透過定義 Module 類別,您已讓您的元件可以使用執行時期 DI 架構,例如 Google Guice 或 Spring。Scala 中流行的替代方案是 編譯時期 DI。若要讓您的元件也可以與編譯時期 DI 一起使用,請提供宣告元件相依性的 Scala trait。以下是您如何為執行範例執行此操作

import play.api.inject.ApplicationLifecycle

trait MyComponents {
  def applicationLifecycle: ApplicationLifecycle
  lazy val component: MyComponent = new MyComponentImpl(applicationLifecycle)
}

§刪除您的 Plugin 類別和 play.plugins 檔案

在這個時候,您應該已經成功地遷移您的程式碼,因此是時候刪除您的 Plugin 類別和 play.plugins 檔案了。後者通常位於專案的 conf 資料夾中。

下一步:Reactive Streams 整合(實驗性質)


在此文件發現錯誤?此頁面的原始程式碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時提出拉取請求。有問題或建議要分享?請前往 我們的社群論壇 與社群展開對話。