![](/img/trans.png)
[英]How to use dependency injection in Scala without Play Framework?
[英]Scala Dependency Injection in Play Framework
我是Scala,PlayFramework和依赖注入的新手。 我下载了示例scala play框架代码。 有人可以向我解释为什么我们需要注入Clock和appLifeCycle吗? 上面已经提到了它,因此不需要注入它吗? 这里发生了什么? 为什么我们通常需要对Web框架执行此操作?
package services
import java.time.{Clock, Instant}
import javax.inject._
import play.api.Logger
import play.api.inject.ApplicationLifecycle
import scala.concurrent.Future
/**
* This class demonstrates how to run code when the
* application starts and stops. It starts a timer when the
* application starts. When the application stops it prints out how
* long the application was running for.
*
* This class is registered for Guice dependency injection in the
* [[Module]] class. We want the class to start when the application
* starts, so it is registered as an "eager singleton". See the code
* in the [[Module]] class to see how this happens.
*
* This class needs to run code when the server stops. It uses the
* application's [[ApplicationLifecycle]] to register a stop hook.
*/
@Singleton
class ApplicationTimer @Inject() (clock: Clock, appLifecycle: ApplicationLifecycle) {
// This code is called when the application starts.
private val start: Instant = clock.instant
Logger.info(s"ApplicationTimer demo: Starting application at $start.")
// When the application starts, register a stop hook with the
// ApplicationLifecycle object. The code inside the stop hook will
// be run when the application stops.
appLifecycle.addStopHook { () =>
val stop: Instant = clock.instant
val runningTime: Long = stop.getEpochSecond - start.getEpochSecond
Logger.info(s"ApplicationTimer demo: Stopping application at ${clock.instant} after ${runningTime}s.")
Future.successful(())
}
}
我假设您正在使用Lightbend的Play Scala Seed ,其中包含您发布的代码示例。
如果查看java.time.Clock的文档,您会注意到它说(强调我):
应用程序的最佳实践是将Clock传递到需要当前时刻的任何方法中。 依赖项注入框架是实现此目标的一种方法。 {..省略代码示例..}这种方法允许在测试过程中使用备用时钟 ,例如固定时钟或偏移时钟 。
最终依赖注入的目的是允许您定义要注入类或对象的接口 ,并配置在短短的一个地方是接口的实现。 另一种选择是必须更新多个文件中的硬编码依赖关系,这可能是混乱且容易出错的。 在Play Scala Seed项目中,您会注意到一个名为app/Module.scala
的文件。 该文件是您可以配置绑定的地方,它们将在应用程序启动时自动绑定。 注意我们绑定Clock
实现的行:
class Module extends AbstractModule {
override def configure() = {
// Use the system clock as the default implementation of Clock
bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)
// Ask Guice to create an instance of ApplicationTimer when the
// application starts.
bind(classOf[ApplicationTimer]).asEagerSingleton()
// Set AtomicCounter as the implementation for Counter.
bind(classOf[Counter]).to(classOf[AtomicCounter])
}
}
此配置表示“当我的应用程序启动时,无论我在哪里注入Clock
都应使用Clock.systemDefaultZone
。” 如果希望ApplicationTimer
在测试期间使用其他时钟,则可以执行以下操作:
import play.api.{Environment, Mode}
// Notice that we include the environment
class Module(environment: Environment) extends AbstractModule {
override def configure() = {
// Use the system clock as the default implementation of Clock
environment.mode match {
case Mode.Prod | Mode.Dev => {
bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)
}
case Mode.Test => {
// Specifically use UTC clock in tests, because maybe it's easier to write assertions that way
// You could inject anything here and the ApplicationTimer would use it during tests
bind(classOf[Clock]).toInstance(Clock.systemUTC())
}
}
bind(classOf[ApplicationTimer]).asEagerSingleton()
bind(classOf[Counter]).to(classOf[AtomicCounter])
}
}
您可以在根包的其他位置定义模块(即,文件package com.example.whatever
不在文件顶部的任何声明),它们也将自动加载。 否则,您需要在conf/application.conf
中添加一个绑定,以将模块的名称添加到play.modules.enabled
键中。 您可以在Play Scala种子中看到已注释掉的示例。 我还在这里写的另一个答案中做得更深入。
至于ApplicationLifecycle
,这是Play提供的特殊模块,您可以使用自己的绑定重写它,但是我不确定为什么要这么做。 它可以让您访问在应用程序关闭之前执行的挂钩。 再次,注入该组件是因为将其交换出来很简单。 想象有100个模块,它们全部取决于应用程序生命周期。 默认情况下,它绑定到DefaultApplicationLifecycle
(您可以在此处看到它的绑定)。 如果您已在所有100个模块中对DefaultApplicationLifecycle
进行了硬编码, DefaultApplicationLifecycle
切换到其他生命周期,则必须更新每个模块。 使用依赖注入,您只需要配置绑定以使用不同的生命周期,并且100个模块将自动使用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.