为什么在 Play Framework 中使用 @Singleton 而不是 Scala 的对象?
Why use @Singleton over Scala's object in Play Framework?
我一直在使用Play! Framework for Scala for nearly a year now. I am currently using version 2.5.x。
我知道 Play 中控制器的演变以及开发人员如何被迫远离静态 object
路由。
我也知道 Guice 在游戏中的用法。
如果您下载 activator 和 运行:
activator new my-test-app play-scala
Activator 将为您生成一个模板项目。
我的问题专门针对该模板的 this 文件。
my-test-app/app/services/Counter.scala
package services
import java.util.concurrent.atomic.AtomicInteger
import javax.inject._
/**
* This trait demonstrates how to create a component that is injected
* into a controller. The trait represents a counter that returns a
* incremented number each time it is called.
*/
trait Counter {
def nextCount(): Int
}
/**
* This class is a concrete implementation of the [[Counter]] trait.
* It is configured for Guice dependency injection in the [[Module]]
* class.
*
* This class has a `Singleton` annotation because we need to make
* sure we only use one counter per application. Without this
* annotation we would get a new instance every time a [[Counter]] is
* injected.
*/
@Singleton
class AtomicCounter extends Counter {
private val atomicCounter = new AtomicInteger()
override def nextCount(): Int = atomicCounter.getAndIncrement()
}
你也可以在this文件中看到它的用法:
my-test-app/app/controllers/CountController.scala
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import services.Counter
/**
* This controller demonstrates how to use dependency injection to
* bind a component into a controller class. The class creates an
* `Action` that shows an incrementing count to users. The [[Counter]]
* object is injected by the Guice dependency injection system.
*/
@Singleton
class CountController @Inject() (counter: Counter) extends Controller {
/**
* Create an action that responds with the [[Counter]]'s current
* count. The result is plain text. This `Action` is mapped to
* `GET /count` requests by an entry in the `routes` config file.
*/
def count = Action { Ok(counter.nextCount().toString) }
}
这意味着每个具有 @Inject() (counter: Counter)
构造函数的控制器都将接收相同的 Counter
实例。
所以我的问题是:
为什么要使用 @Singleton
然后 @Inject
将其放入控制器中,对于此示例,您什么时候可以只使用 Scala 对象?
它的代码少了很多。
示例:
my-test-app/app/services/Counter.scala
package services
trait ACounter {
def nextCount: Int
}
object Counter with ACounter {
private val atomicCounter = new AtomicInteger()
def nextCount(): Int = atomicCounter.getAndIncrement()
}
像这样使用它:
my-test-app/app/controllers/CountController.scala
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import services.{Counter, ACounter}
/**
* This controller demonstrates how to use dependency injection to
* bind a component into a controller class. The class creates an
* `Action` that shows an incrementing count to users. The [[Counter]]
* object is injected by the Guice dependency injection system.
*/
@Singleton
class CountController extends Controller {
//depend on abstractions
val counter: ACounter = Counter
def count = Action { Ok(counter.nextCount().toString) }
}
有什么区别?注射是首选吗?为什么?
注入是首选方式吗?一般是
使用依赖注入的几个优点:
- 将控制器与
Counter
的具体实现分离。
- 如果您要使用
object
,则必须更改您的控制器以指向不同的实现。 EG Counter2.nextCount().toString
- 您可以在测试期间使用 Guice 自定义绑定改变实现
- 假设您在
Counter
内部进行 WS
调用。这可能会给单元测试带来一些困难。如果您在 Guice 中使用依赖注入,您可以覆盖 Counter
和 AtomicCounter
之间的绑定以指向您专门为测试编写的 Counter
的离线版本。有关使用 Guice 进行 Play 测试的更多信息,请参阅 here。
另请参阅 motivations Play 迁移到 DI 的过程。
我说一般是因为我看到依赖注入在使用 Spring 和其他 Java 框架时出现了可怕的错误。我会说你应该使用你自己的判断,但在使用 DI 玩游戏方面是错误的。
我不确定,如果我理解你的问题,但首选注入,因为:
- 应用程序的不同部分耦合度较低
- 用提供相同功能的不同 class 替换您的依赖项更容易(以防您将来需要这样做)- 您将需要更改几行代码而不是查找所有代码您的对象出现次数
- 测试更简单(尤其是当你需要模拟某些东西时)
简而言之:D 来自 SOLID 原则:"Depend upon Abstractions. Do not depend upon concretions".
可能是因为Scala的单例对象不能有参数?例如,如果你有一个注入了 DAO 的服务 class,并且你想在控制器中使用服务,你必须注入它。最简单的方法(IMO)是使用 Guice 进行 DI...此外,您可以将依赖项放在一个地方(模块)等...
我一直在使用Play! Framework for Scala for nearly a year now. I am currently using version 2.5.x。
我知道 Play 中控制器的演变以及开发人员如何被迫远离静态 object
路由。
我也知道 Guice 在游戏中的用法。
如果您下载 activator 和 运行:
activator new my-test-app play-scala
Activator 将为您生成一个模板项目。 我的问题专门针对该模板的 this 文件。
my-test-app/app/services/Counter.scala
package services
import java.util.concurrent.atomic.AtomicInteger
import javax.inject._
/**
* This trait demonstrates how to create a component that is injected
* into a controller. The trait represents a counter that returns a
* incremented number each time it is called.
*/
trait Counter {
def nextCount(): Int
}
/**
* This class is a concrete implementation of the [[Counter]] trait.
* It is configured for Guice dependency injection in the [[Module]]
* class.
*
* This class has a `Singleton` annotation because we need to make
* sure we only use one counter per application. Without this
* annotation we would get a new instance every time a [[Counter]] is
* injected.
*/
@Singleton
class AtomicCounter extends Counter {
private val atomicCounter = new AtomicInteger()
override def nextCount(): Int = atomicCounter.getAndIncrement()
}
你也可以在this文件中看到它的用法:
my-test-app/app/controllers/CountController.scala
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import services.Counter
/**
* This controller demonstrates how to use dependency injection to
* bind a component into a controller class. The class creates an
* `Action` that shows an incrementing count to users. The [[Counter]]
* object is injected by the Guice dependency injection system.
*/
@Singleton
class CountController @Inject() (counter: Counter) extends Controller {
/**
* Create an action that responds with the [[Counter]]'s current
* count. The result is plain text. This `Action` is mapped to
* `GET /count` requests by an entry in the `routes` config file.
*/
def count = Action { Ok(counter.nextCount().toString) }
}
这意味着每个具有 @Inject() (counter: Counter)
构造函数的控制器都将接收相同的 Counter
实例。
所以我的问题是:
为什么要使用 @Singleton
然后 @Inject
将其放入控制器中,对于此示例,您什么时候可以只使用 Scala 对象?
它的代码少了很多。
示例:
my-test-app/app/services/Counter.scala
package services
trait ACounter {
def nextCount: Int
}
object Counter with ACounter {
private val atomicCounter = new AtomicInteger()
def nextCount(): Int = atomicCounter.getAndIncrement()
}
像这样使用它:
my-test-app/app/controllers/CountController.scala
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import services.{Counter, ACounter}
/**
* This controller demonstrates how to use dependency injection to
* bind a component into a controller class. The class creates an
* `Action` that shows an incrementing count to users. The [[Counter]]
* object is injected by the Guice dependency injection system.
*/
@Singleton
class CountController extends Controller {
//depend on abstractions
val counter: ACounter = Counter
def count = Action { Ok(counter.nextCount().toString) }
}
有什么区别?注射是首选吗?为什么?
注入是首选方式吗?一般是
使用依赖注入的几个优点:
- 将控制器与
Counter
的具体实现分离。- 如果您要使用
object
,则必须更改您的控制器以指向不同的实现。 EGCounter2.nextCount().toString
- 如果您要使用
- 您可以在测试期间使用 Guice 自定义绑定改变实现
- 假设您在
Counter
内部进行WS
调用。这可能会给单元测试带来一些困难。如果您在 Guice 中使用依赖注入,您可以覆盖Counter
和AtomicCounter
之间的绑定以指向您专门为测试编写的Counter
的离线版本。有关使用 Guice 进行 Play 测试的更多信息,请参阅 here。
- 假设您在
另请参阅 motivations Play 迁移到 DI 的过程。
我说一般是因为我看到依赖注入在使用 Spring 和其他 Java 框架时出现了可怕的错误。我会说你应该使用你自己的判断,但在使用 DI 玩游戏方面是错误的。
我不确定,如果我理解你的问题,但首选注入,因为:
- 应用程序的不同部分耦合度较低
- 用提供相同功能的不同 class 替换您的依赖项更容易(以防您将来需要这样做)- 您将需要更改几行代码而不是查找所有代码您的对象出现次数
- 测试更简单(尤其是当你需要模拟某些东西时)
简而言之:D 来自 SOLID 原则:"Depend upon Abstractions. Do not depend upon concretions".
可能是因为Scala的单例对象不能有参数?例如,如果你有一个注入了 DAO 的服务 class,并且你想在控制器中使用服务,你必须注入它。最简单的方法(IMO)是使用 Guice 进行 DI...此外,您可以将依赖项放在一个地方(模块)等...