Scala(Play 2.4.x) Cómo llamar a una clase con la anotación @inject()
playframework annotations (4)
Estoy viendo el ejemplo de código escamoso de play-mailer: https://github.com/playframework/play-mailer
Básicamente es así:
class MyComponent @Inject() (mailerClient: MailerClient) {
...
}
lo suficientemente simple y compila sin cumplir
Luego trato de "llamar" sin embargo y no parece haber una manera de satisfacer al compilador O obtener una instancia de trabajo de mailerClient.
object AnObject {
val mailer = new MyComponent
def sendEmail = mailer.doStuff
}
[info] Compiling 1 Scala source to ...
[error] /SomeOne/SomePath/SomeFile.scala:30: not enough arguments for constructor MyComponent: (mailerClient: play.api.libs.mailer.MailerClient) MyComponent.
[error] Unspecified value parameter mailerClient.
[error] val mailer = new MyComponent
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
Pensé que podría haberme acercado gracias a esto:
¿Cómo funciona @Inject en Scala?
Lo que indicaba que la siguiente sintaxis podría funcionar al eliminar el @Inject
del constructor y colocarlo en un campo.
@Inject var mailerClient: MailerClient = null
Sin embargo, en el momento en que intentamos ejecutar cualquier cosa que necesite esa referencia, obtenemos nulo.
Estoy leyendo todo lo que puedo encontrar en @Inject
([advertencia] [rant] NO soy fanático de la magia de compilación como esta por esta razón exacta: la magia vudú es maravillosa hasta que deja de funcionar, entonces nadie parece tener idea de cómo solucionarlo. [/ rant] [/advertencia] )
pero lo que realmente quiero saber es cómo usarlo de manera adecuada, segura y efectiva.
(No tengo suficiente reputación para comentar, por lo que publicar como respuesta)
La respuesta publicada por @aparo debe marcarse como la respuesta corect / aprobada, porque resuelve el problema. Usted dijo que esto no resuelve la pregunta original porque mueve la dependencia a otra clase, pero esto solo es parcialmente cierto, ya que esa otra clase solo necesitará proporcionarle un MyComponent
lugar de un MailerClient
. Aunque la inyección de dependencia necesita ser utilizada hasta el controlador final (en el caso de Play), generalmente no tendrá que inyectar más de un solo objeto.
Prueba de esto se puede ver en una pregunta que publiqué , ya que tenía la misma mentalidad que tú en ese momento. En mi ejemplo, mi controlador solo necesita una dependencia de UserSearch
, la dependencia DatabaseConfigurationProvider
es tratada por guice, por lo que no es necesario que vuelva a mencionarla en ningún otro lugar.
Tuve el mismo problema. Quiero crear una clase u objeto que tenga la funcionalidad de correo y luego puedo llamarlo cuando quiera enviar correos electrónicos desde cualquier controlador.
Creo que lo que estaba preguntando es equivalente a cómo usar el mailerclient fuera del marco de juego. Por lo que yo entiendo, no puedes. Incluso cuando creas una clase en la carpeta de la aplicación / controladores, es solo una clase scala normal que no tiene nada que ver con la magia en el marco de juego. @Inject funciona solo con controladores o módulos. Porque si creas una clase independiente, tienes que inyectar algo tú solo cuando la instancias. A menos que esté construyendo un módulo o extendiendo un controlador. ¿Notaste que el AppController solía ser un objeto y ahora es una clase? No necesita instanciar eso cuando lo usa. Creo que el framework hizo algo para crear una instancia con la configuración (inyectado).
Entonces, si quiere alcanzar el objetivo de diseño original, puede escribir un módulo, publicarlo y luego usarlo en todos los controladores; o use algunas otras bibliotecas de correo electrónico en Scala para finalizar la funcionalidad de envío de correo electrónico.
Ya que cerró su problema en el repositorio original de GitHub, no sé si esta respuesta todavía es necesaria, pero como no comprende completamente el uso de un marco DI y considero que es muy importante aprender esta habilidad, lo haré. intenta explicarlo aquí y enumera algunos beneficios.
En primer lugar, la forma en que está instanciando su instancia no le da al marco DI la posibilidad de inyectar sus dependencias. Como new
es una palabra clave de idioma, DI no puede interferir y las dependencias que necesita para su clase no se pueden inyectar. Cómo se hace es a través de la inyección de constructor o campo. Principalmente me concentraré en la inyección de constructores porque eso es "estándar" en el mundo scala.
Si especifica un argumento constructor con la anotación @Injected
, básicamente le está diciendo al marco DI que resuelva esta dependencia del contenedor. El marco DI va y busca una entrada de ese objeto dentro de su contenedor. Si no existe, lo creará (y resolverá sus dependencias en el proceso) y si está anotado con @Singleton
también guardará esta instancia para uso futuro. La mayoría de los marcos DI requieren que especifique una clase de inicio en la mayoría de los casos, ¡pero porque está usando Play! Marco esto no es necesario. Cuando desee utilizar un módulo particular dentro de su controlador, puede hacer esto:
import javax.inject.Inject
import play.api.mvc.Controller
class Test @Inject() (val dependency: FooClass) extends Controller {
...
}
En este caso FooClass
es el nombre de clase de la dependencia que desea inyectar en su controlador. Digamos que FooClass
tiene la Application
Play como una dependencia que se inyectará, porque Play proporciona un par de preajustes pre-enlazados como Application
pero también ActorSystem
.
Esto es posible porque Play! Framework usa DependencyInjectedRoutes
. Si tuviera que crear un Actor fuera de un Controlador, debería especificarlo dentro de una clase de módulo, pero eso se explica en este enlace y en este enlace .
También existe el concepto de usar Traits
dentro de su controlador y luego conectar las características con las clases de implementación, pero creo que es un poco complicado por ahora.
Si quiere algunos beneficios y historias exitosas para este método de escribir aplicaciones, aquí hay un buen recurso: https://softwareengineering.stackexchange.com/a/19204/164366
Si quieres leer algo sobre este concepto:
- https://www.playframework.com/documentation/2.4.x/ScalaAkka
- https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
- https://www.playframework.com/documentation/2.4.x/ScalaCompileTimeDependencyInjection
¡Espero que esto aclare las cosas! Si tiene alguna pregunta, ¡por favor pregunte!
esto no es un Scala Issues, sino un DI. Deberías leer alguna documentación de Guice .
En Play 2.4.x, debe usar la inyección de dependencia ( https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection ) para lograr su objetivo.
Su AnObject debe ser:
@Singleton class AnObject @Inject()(mailer:MyComponent){
def sendEmail = mailer.doStuff
}