我正在使用Scala Play 2.6并尝试使用依赖注入来根据请求参数实例化服务类。如下面的示例代码,控制器类从查询字符串
获取付款方法package controllers
import com.google.inject.Inject
import play.api.mvc._
import scala.concurrent.ExecutionContext
class PaymentController @Inject()()
(implicit ec: ExecutionContext)
extends InjectedController {
def doPayment() = Action.async { implicit request =>
request.getQueryString("payment-method").getOrElse("") match {
case "paypal" => // Inject a PaypalPaymentService
val paymentService = Play.current.injector.instanceOf[PaypalPaymentService]
paymentService.processPayment()
case "creditcard" => // Inject a CreditCardPaymentService
val paymentService = Play.current.injector.instanceOf[CreditCardPaymentService]
paymentService.processPayment()
case _ => // Return error
}
}
}
用于处理Paypal或CreditCard付款的服务类
package services
import scala.concurrent.Future
trait PaymentService {
def processPayment(): Future[Boolean]
}
package services
import com.google.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.ws.WSClient
class PaypalPaymentService @Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process paypal payment
}
}
class CreditCardPaymentService @Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process credit card payment
}
}
对于Play 2.5以上版本,已弃用Play.current
和Play.application
。
我有两个问题:
答案 0 :(得分:1)
您已正确说明Unhandled rejection Error: Error invoking Compose. Check the inner error for details.
------------------------------------------------
Inner Error:
Message: Constructor Parameter with index 0 cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?
Inner Error Stack:
Error: Constructor Parameter with index 0 cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?
at Object.invokeWithDynamicDependencies (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35219:13)
at InvocationHandler.invoke (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35204:166)
at Container.invoke (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35475:23)
at ProviderResolver.get (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31046:72)
at Container.get (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35414:21)
at Container.elementContainerGet [as get] (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31101:15)
at HtmlBehaviorResource.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:33088:56)
at applyInstructions (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31203:31)
at ViewFactory.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31424:7)
at BoundViewFactory.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31304:33)
at If._show (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:36666:36)
at If.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:73912:12)
at Controller.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32473:22)
at View.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:30462:22)
at Controller.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32465:17)
at Controller.automate (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32410:10)
End Inner Error Stack
------------------------------------------------
at new AggregateError (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:8478:11)
at Container.invoke (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35477:13)
at ProviderResolver.get (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31046:72)
at Container.get (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:35414:21)
at Container.elementContainerGet [as get] (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31101:15)
at HtmlBehaviorResource.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:33088:56)
at applyInstructions (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31203:31)
at ViewFactory.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31424:7)
at BoundViewFactory.create (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:31304:33)
at If._show (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:36666:36)
at If.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:73912:12)
at Controller.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32473:22)
at View.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:30462:22)
at Controller.bind (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32465:17)
at Controller.automate (http://localhost:9000/common.595063c809425d78a6ec.bundle.js:32410:10)
at http://localhost:9000/common.595063c809425d78a6ec.bundle.js:33554:18
和Play.current
已被弃用,从2.5开始,使用它们的方式确实是通过注入它们。
我会更改您的控制器定义,以便您使用DI来包含所需的组件。类似的东西:
Play.application
现在是棘手的部分。您可能只是注入class PaymentController @Inject()(configuration: Configuration)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
,但这并不完全正确,因为您将遇到循环依赖。这是正常的,因为您希望在实际存在的同时注入整个应用程序。这有一个 hack ,它是通过注入application: play.Application
。我称之为 hack ,因为通常你不需要/想要注入整个应用程序。在99%的情况下,您只对特定部分感兴趣 - 例如Provider[Application]
,Configuration
等
这就是解决方案。您可以注入Environment
Injector
从这里开始游戏很简单。只需使用class PaymentController @Inject()(injector: Injector)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
即可获得所需的服务。像这样:
Injector
关于使用此方法的“正确方法”的最后一句话。我实际上发现你的方法没问题,也不一定会改变它。在这个方向上只有一个想法是你创建一个case "paypal" => // Inject a PaypalPaymentService
val paymentService = injector.instanceOf(classOf[PaypalPaymentService])
paymentService.processPayment()
这样:
Module
具有共同特征(就像你这样做)有助于这种情况,你可以有多个实现,甚至是模拟的测试。如果模块位于根包中,则会自动注册该模块。否则你应该告诉Play它的位置:
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class PaymentModule extends AbstractModule {
def configure() = {
bind(classOf[PaymentService])
.annotatedWith(Names.named("paypal"))
.to(classOf[PaypalPaymentService])
bind(classOf[PaymentService])
.annotatedWith(Names.named("creditcard"))
.to(classOf[CreditCardPaymentService])
}
}