Pourquoi utiliser @Singleton sur L'objet de Scala dans Play Framework?
j'ai utilisé Jouer! Framework Scala depuis près d'un an maintenant. J'utilise actuellement la version 2.5.x.
je suis conscient de l'évolution des contrôleurs en jeu et de la façon dont les développeurs ont été forcés de s'éloigner de la statique object
itinéraires.
je suis également conscient de l' Guice utilisation en jeu.
Si vous téléchargez activateur et exécuter:
activator new my-test-app play-scala
activateur produira un projet de modèle pour vous. Ma question est spécifiquement autour de fichier de ce modèle.
mon-test-app/app/services/Compteur.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()
}
vous pouvez aussi voir son utilisation dans fichier:
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) }
}
cela signifie Chaque contrôleur qui a le constructeur de @Inject() (counter: Counter)
recevrez la même instance de Counter
.
alors ma question Est:
Pourquoi utiliser @Singleton
et @Inject
dans un contrôleur, quand dans cet exemple vous pouvez juste utiliser un objet Scala?
Son beaucoup moins de code.
Exemple:
mon-test-app/app/services/Compteur.scala
package services
trait ACounter {
def nextCount: Int
}
object Counter with ACounter {
private val atomicCounter = new AtomicInteger()
def nextCount(): Int = atomicCounter.getAndIncrement()
}
Utiliser comme donc:
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) }
}
Quelle est la différence? L'injection est-elle préférable, et pourquoi?
3 réponses
l'injection est-elle la méthode préférée? Généralement oui
quelques avantages de l'injection de dépendances:
- découpler le contrôleur de la mise en œuvre concrète de
Counter
.- si vous utilisez un
object
, Vous devriez changer votre controller pour pointer vers l'implémentation différente. Par exempleCounter2.nextCount().toString
- si vous utilisez un
- vous pouvez modifier l'implémentation pendant les tests en utilisant des fixations personnalisées de Guice
- Permet de dire qu'à l'intérieur de
Counter
tu fais unWS
appel. Cela pourrait causer des difficultés dans les tests unitaires. Si vous utilisez l'injection de dépendances avec Guice, vous pouvez annuler la liaison entreCounter
etAtomicCounter
pour pointer vers une version hors ligne deCounter
que vous avez écrit spécifiquement pour vos tests. Voir ici pour plus d'informations sur L'utilisation de Guice pour les tests Play.
- Permet de dire qu'à l'intérieur de
voir Aussi motivations que le Jeu avait pour la migration pour DI.
je dis généralement parce que j'ai vu l'injection de dépendances se passer horriblement mal en utilisant Spring et d'autres cadres Java. Je dirais que vous devriez utiliser votre propre jugement mais errer du côté de L'utilisation de DI pour jouer.
peut-être parce que L'objet singleton de Scala ne peut pas avoir de paramètres? Par exemple, si vous avez une classe de service qui a un DAO injecté, et que vous voulez utiliser service dans controller, vous devez l'injecter. La manière la plus facile(IMO) est DI avec Guice... En outre, vous pouvez avoir vos dépendances dans un endroit(module) etc...
je ne suis pas sûr, si je comprends bien votre question, mais l'injection est préféré parce que:
- les différentes parties de votre application sont moins couplée
- il est plus facile de remplacer votre dépendance par une classe différente offrant la même fonctionnalité (au cas où vous auriez besoin de le faire à l'avenir) - vous devrez changer quelques lignes de code et ne pas chercher toutes les occurrences de votre objet
- il est plus simple de tester (surtout quand vous devez vous moquer quelque chose)
bref: D des principes solides: "dépendent des Abstractions. Ne dépendent pas de concrétions".