Play 2.4 WebSocket 在使用编译时依赖注入时抛出 InstantiationException
Play 2.4 WebSocket throws InstantiationException when using compile-time dependency injection
Play 2.4 中的 WebSockets 在使用编译时依赖注入时似乎不起作用。我使用的是最新版本 2.4.2。具体来说,它在连接时抛出以下异常:
play.api.http.HttpErrorHandlerExceptions$$anon: Execution exception[[InstantiationException: null]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:254) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:180) ~[play_2.11-2.4.0.jar:2.4.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$messageReceived.applyOrElse(PlayDefaultUpstreamHandler.scala:182) [play-netty-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$messageReceived.applyOrElse(PlayDefaultUpstreamHandler.scala:180) [play-netty-server_2.11-2.4.0.jar:2.4.0]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.6.jar:na]
at scala.util.Failure$$anonfun$recover.apply(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.util.Try$.apply(Try.scala:191) [scala-library-2.11.6.jar:na]
at scala.util.Failure.recover(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.6.jar:na]
...
Caused by: java.lang.InstantiationException: null
at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48) ~[na:1.8.0_45]
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) ~[na:1.8.0_45]
at java.lang.Class.newInstance(Class.java:442) ~[na:1.8.0_45]
at play.api.inject.NewInstanceInjector$.instanceOf(Injector.scala:49) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.inject.SimpleInjector$$anonfun$instanceOf.apply(Injector.scala:85) ~[play_2.11-2.4.0.jar:2.4.0]
at scala.collection.MapLike$class.getOrElse(MapLike.scala:128) ~[scala-library-2.11.6.jar:na]
at scala.collection.AbstractMap.getOrElse(Map.scala:59) ~[scala-library-2.11.6.jar:na]
at play.api.inject.SimpleInjector.instanceOf(Injector.scala:85) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.inject.SimpleInjector.instanceOf(Injector.scala:80) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.Application$$anonfun$instanceCache.apply(Application.scala:234) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.Application$$anonfun$instanceCache.apply(Application.scala:234) ~[play_2.11-2.4.0.jar:2.4.0]
at play.utils.InlineCache.fresh(InlineCache.scala:69) ~[play_2.11-2.4.0.jar:2.4.0]
at play.utils.InlineCache.apply(InlineCache.scala:62) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.libs.concurrent.Akka$.system(Akka.scala:37) ~[play_2.11-2.4.0.jar:2.4.0]
...
控制器的外观如下:
class Application extends Controller {
import play.api.Play.current
def socket = WebSocket.acceptWithActor[String, String] { request => out =>
WebSocketActor.props(out)
}
}
object WebSocketActor {
def props(out: ActorRef) = Props(new WebSocketActor(out))
}
class WebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("Received message: " + msg)
}
}
下面是 ApplicationLoader
的样子:
class AppLoader extends ApplicationLoader {
override def load(context: Context): Application = new AppComponents(context).application
}
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) {
lazy val appController = new controllers.Application
lazy val router = new Routes(httpErrorHandler, appController, assets)
lazy val assets = new controllers.Assets(httpErrorHandler)
}
原来这是 Play 2.4.2 中的错误。目前有一个 pull request 可以修复它。
在它被修复之前,我通过覆盖 AppComponents
的 injector
来解决它,同时也包括 actorSystem
。所以 AppComponents
变成:
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) {
lazy val appController = new controllers.Application
lazy val router = new Routes(httpErrorHandler, appController, assets)
lazy val assets = new controllers.Assets(httpErrorHandler)
// temporary workaround until issue #4614 in playframework is fixed. See https://github.com/playframework/playframework/issues/4614
override lazy val injector = new SimpleInjector(NewInstanceInjector) + router.asInstanceOf[play.api.routing.Router] + crypto + httpConfiguration + actorSystem
}
Play 2.4 中的 WebSockets 在使用编译时依赖注入时似乎不起作用。我使用的是最新版本 2.4.2。具体来说,它在连接时抛出以下异常:
play.api.http.HttpErrorHandlerExceptions$$anon: Execution exception[[InstantiationException: null]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:254) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:180) ~[play_2.11-2.4.0.jar:2.4.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$messageReceived.applyOrElse(PlayDefaultUpstreamHandler.scala:182) [play-netty-server_2.11-2.4.0.jar:2.4.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$messageReceived.applyOrElse(PlayDefaultUpstreamHandler.scala:180) [play-netty-server_2.11-2.4.0.jar:2.4.0]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.6.jar:na]
at scala.util.Failure$$anonfun$recover.apply(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.util.Try$.apply(Try.scala:191) [scala-library-2.11.6.jar:na]
at scala.util.Failure.recover(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.6.jar:na]
...
Caused by: java.lang.InstantiationException: null
at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48) ~[na:1.8.0_45]
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) ~[na:1.8.0_45]
at java.lang.Class.newInstance(Class.java:442) ~[na:1.8.0_45]
at play.api.inject.NewInstanceInjector$.instanceOf(Injector.scala:49) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.inject.SimpleInjector$$anonfun$instanceOf.apply(Injector.scala:85) ~[play_2.11-2.4.0.jar:2.4.0]
at scala.collection.MapLike$class.getOrElse(MapLike.scala:128) ~[scala-library-2.11.6.jar:na]
at scala.collection.AbstractMap.getOrElse(Map.scala:59) ~[scala-library-2.11.6.jar:na]
at play.api.inject.SimpleInjector.instanceOf(Injector.scala:85) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.inject.SimpleInjector.instanceOf(Injector.scala:80) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.Application$$anonfun$instanceCache.apply(Application.scala:234) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.Application$$anonfun$instanceCache.apply(Application.scala:234) ~[play_2.11-2.4.0.jar:2.4.0]
at play.utils.InlineCache.fresh(InlineCache.scala:69) ~[play_2.11-2.4.0.jar:2.4.0]
at play.utils.InlineCache.apply(InlineCache.scala:62) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.libs.concurrent.Akka$.system(Akka.scala:37) ~[play_2.11-2.4.0.jar:2.4.0]
...
控制器的外观如下:
class Application extends Controller {
import play.api.Play.current
def socket = WebSocket.acceptWithActor[String, String] { request => out =>
WebSocketActor.props(out)
}
}
object WebSocketActor {
def props(out: ActorRef) = Props(new WebSocketActor(out))
}
class WebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("Received message: " + msg)
}
}
下面是 ApplicationLoader
的样子:
class AppLoader extends ApplicationLoader {
override def load(context: Context): Application = new AppComponents(context).application
}
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) {
lazy val appController = new controllers.Application
lazy val router = new Routes(httpErrorHandler, appController, assets)
lazy val assets = new controllers.Assets(httpErrorHandler)
}
原来这是 Play 2.4.2 中的错误。目前有一个 pull request 可以修复它。
在它被修复之前,我通过覆盖 AppComponents
的 injector
来解决它,同时也包括 actorSystem
。所以 AppComponents
变成:
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) {
lazy val appController = new controllers.Application
lazy val router = new Routes(httpErrorHandler, appController, assets)
lazy val assets = new controllers.Assets(httpErrorHandler)
// temporary workaround until issue #4614 in playframework is fixed. See https://github.com/playframework/playframework/issues/4614
override lazy val injector = new SimpleInjector(NewInstanceInjector) + router.asInstanceOf[play.api.routing.Router] + crypto + httpConfiguration + actorSystem
}