§編譯時間依賴性注入
Play 提供了一種執行時期依賴性注入機制,也就是在執行時期才連結依賴性的依賴性注入。這種方法有優缺點,主要優點是減少樣板程式碼,主要缺點是應用程式的建構並未在編譯時期驗證。
另一種方法是使用編譯時間依賴性注入。最簡單來說,編譯時間 DI 可以透過手動建構並連結依賴性來達成。還有其他更進階的技術和工具,例如 Dagger。這些技術和工具都可以輕鬆地實作在建構函式和手動連結上,因此 Play 對編譯時間依賴性注入的支援是提供公開建構函式和工廠方法作為 API。
注意:如果你不熟悉編譯時間 DI 或一般的 DI,建議閱讀 Adam Warski 的 Scala 的 DI 指南,其中討論了編譯時間 DI 的一般概念。雖然這是針對 Scala 開發人員的說明,但也可以讓你了解編譯時間注入的優點。
除了提供公開建構函式和工廠方法外,所有 Play 的現成模組都提供一些實作蛋糕模式輕量級形式的介面,以提供便利性。這些介面建立在公開建構函式的基礎上,而且完全是選用的。在某些應用程式中,它們可能不適合使用,但在許多應用程式中,它們會是很方便的機制,可用於連接 Play 提供的元件。這些介面的命名慣例是使用 Components
結尾,例如,DB API 的預設 HikariCP 實作提供一個稱為 HikariCPComponents 的介面。
注意:當然,Java 有些限制,無法完全實作蛋糕模式。例如,您無法在介面中擁有狀態。
在以下範例中,我們將展示如何使用內建元件輔助介面手動連接 Play 應用程式。透過閱讀提供的元件介面的原始碼,也可以很輕鬆地將此適應到其他依賴注入技術。
§應用程式進入點
在 JVM 上執行的每個應用程式都需要一個由反射載入的進入點,即使您的應用程式自行啟動,主類別仍會由反射載入,而且其主方法會使用反射來尋找和呼叫。
在 Play 的開發模式中,Play 使用的 JVM 和 HTTP 伺服器必須在您的應用程式重新啟動之間持續執行。為了實作這一點,Play 提供一個 ApplicationLoader 介面,您可以實作這個介面。每次重新載入應用程式時,都會建構和呼叫應用程式載入器,以載入應用程式。
此介面的載入方法採用應用程式載入器 Context 作為引數,其中包含 Play 應用程式所需的所有元件,這些元件會比應用程式本身更長久,且無法由應用程式本身建構。其中有許多元件特別用於在開發模式中提供功能,例如,來源對應器允許 Play 錯誤處理常式呈現發生例外狀況的地方的來源程式碼。
最簡單的實作方式是延伸 Play BuiltInComponentsFromContext 抽象類別。此類別採用 context,並根據該 context 提供所有內建元件。您只需要提供一個路由器,讓 Play 能將要求路由到適當的地方。以下是使用空路由器以這種方式建立的最簡單應用程式
import play.Application;
import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.LoggerConfigurator;
import play.controllers.AssetsComponents;
import play.db.ConnectionPool;
import play.db.HikariCPComponents;
import play.filters.components.HttpFiltersComponents;
import play.mvc.Results;
import play.routing.Router;
import play.routing.RoutingDslComponentsFromContext;
public class MyComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents {
public MyComponents(ApplicationLoader.Context context) {
super(context);
}
@Override
public Router router() {
return Router.empty();
}
}
然後是應用程式載入器
public class MyApplicationLoader implements ApplicationLoader {
@Override
public Application load(Context context) {
return new MyComponents(context).application();
}
}
若要設定 Play 使用此應用程式載入器,請設定 play.application.loader
屬性,使其指向 application.conf
檔案中的完全限定類別名稱
play.application.loader=MyApplicationLoader
此外,如果您正在修改使用內建 Guice 模組的現有專案,您應該能夠從 build.sbt
中的 libraryDependencies
中移除 guice
。
§提供路由器
若要設定路由器,您有兩個選項,使用 RoutingDsl
或產生的路由器。
§使用 RoutingDsl
提供路由器
為了讓這更容易,Play 有 RoutingDslComponentsFromContext
類別,它已經提供一個 RoutingDsl
執行個體,使用其他提供的組件建立
public class MyComponentsWithRouter extends RoutingDslComponentsFromContext
implements HttpFiltersComponents {
public MyComponentsWithRouter(ApplicationLoader.Context context) {
super(context);
}
@Override
public Router router() {
// routingDsl method is provided by RoutingDslComponentsFromContext
return routingDsl().GET("/path").routingTo(request -> Results.ok("The content")).build();
}
}
§使用產生的路由器
預設情況下,Play 會使用 注入路由產生器。這會產生一個路由器,其建構函式會接受路由檔案中的每個控制器和包含的路由器,依據它們在路由檔案中出現的順序。路由器的建構函式也會在第一個參數接受 play.api.http.HttpErrorHandler
(play.http.HttpErrorHandler
的 Scala 版本),用於處理參數繫結錯誤,並在最後一個參數接受一個字串前綴。也會提供一個將此預設為 "/"
的重載建構函式。
下列路由
GET / controllers.HomeController.index
GET /assets/*file controllers.Assets.at(path = "/public", file)
會產生一個路由器,它會接受 controllers.HomeController
、controllers.Assets
和任何其他已宣告路由的執行個體。要在實際應用程式中使用這個路由器
public class MyComponentsWithGeneratedRouter extends BuiltInComponentsFromContext
implements HttpFiltersComponents, AssetsComponents {
public MyComponentsWithGeneratedRouter(ApplicationLoader.Context context) {
super(context);
}
@Override
public Router router() {
HomeController homeController = new HomeController();
Assets assets =
new Assets(scalaHttpErrorHandler(), assetsMetadata(), environment().asScala());
return new router.Routes(scalaHttpErrorHandler(), homeController,
return new javaguide.dependencyinjection.Routes(
scalaHttpErrorHandler(), homeController, assets)
.asJava();
}
}
§設定記錄
要在 Play 中正確設定記錄,必須在傳回應用程式之前執行 LoggerConfigurator
。預設的 BuiltInComponentsFromContext 沒有為你呼叫 LoggerConfigurator
。
必須在應用程式載入器中新增這個初始化程式碼
import scala.jdk.javaapi.OptionConverters;
public class MyAppLoaderWithLoggerConfiguration implements ApplicationLoader {
@Override
public Application load(Context context) {
LoggerConfigurator.apply(context.environment().classLoader())
.ifPresent(
loggerConfigurator ->
loggerConfigurator.configure(context.environment(), context.initialConfig()));
return new MyComponents(context).application();
}
}
§使用其他組件
如前所述,Play 提供許多輔助特質,用於連接其他組件。例如,如果你想要使用資料庫連線池,你可以將 HikariCPComponents 混入你的組件 cake,如下所示
public class MyComponentsWithDatabase extends BuiltInComponentsFromContext
implements HikariCPComponents, HttpFiltersComponents {
public MyComponentsWithDatabase(ApplicationLoader.Context context) {
super(context);
}
@Override
public Router router() {
return Router.empty();
}
public SomeComponent someComponent() {
// connectionPool method is provided by HikariCPComponents
return new SomeComponent(connectionPool());
}
}
其他輔助特質也可用作 CSRFComponents 或 AhcWSComponents。提供元件的 Java 介面的完整清單如下:
play.BuiltInComponents
play.components.PekkoComponents
play.components.ApplicationComponents
play.components.BaseComponents
play.components.BodyParserComponents
play.components.ConfigurationComponents
play.components.CryptoComponents
play.components.FileMimeTypesComponents
play.components.HttpComponents
play.components.HttpConfigurationComponents
play.components.HttpErrorHandlerComponents
play.components.TemporaryFileComponents
play.controllers.AssetsComponents
play.i18n.I18nComponents
play.libs.ws.ahc.AhcWSComponents
play.libs.ws.ahc.WSClientComponents
play.cache.ehcache.EhCacheComponents
play.filters.components.AllowedHostsComponents
play.filters.components.CORSComponents
play.filters.components.CSRFComponents
play.filters.components.GzipFilterComponents
play.filters.components.HttpFiltersComponents
play.filters.components.NoHttpFiltersComponents
play.filters.components.RedirectHttpsComponents
play.filters.components.SecurityHeadersComponents
play.routing.RoutingDslComponents
play.data.FormFactoryComponents
play.data.validation.ValidatorsComponents
play.db.ConnectionPoolComponents
play.db.DBComponents
play.db.HikariCPComponents
play.db.jpa.JPAComponents
play.libs.openid.OpenIdComponents
下一頁:應用程式設定
在這個文件中發現錯誤?此頁面的原始碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時提出 pull request。有問題或建議要分享?請前往 我們的社群論壇 與社群開始對話。