Play Framework без внедрения зависимостей? - PullRequest
1 голос
/ 13 января 2020

Не вдаваясь в причину, скажем, кто-то хотел старомодный веб-сервис Play Framework и не хотел использовать внедрение зависимостей или полагаться на Google Guice. Возможно ли это в Play 2.8.x?

Документация API вместе с текущими примерами Play рекомендует это как "типичный" HomeController. scala:

package controllers
import javax.inject._
import play.api.mvc._
class HomeController @Inject() (val controllerComponents: ControllerComponents) extends BaseController {
  def index = Action {
    Ok("It works!")
  }
}

Мой желаемый код такой же, но без @Inject () (похоже на то, когда я последний раз использовал Play 2.4.0 в 2016 году)? Когда-то мой код выглядел так:

package controllers
import play.api.mvc.{Action, AnyContent, Controller}
object TestController {
  def index:Action[AnyContent] = Action {
    Ok("It used to work.")
  }
}

Консоль:

[info] Compiling 1 Scala source to /Volumes/.../play-scala-seed/target/scala-2.13/classes ...
[error] p.a.h.DefaultHttpErrorHandler - 

! @7ef69nl6l - Internal server error, for (GET) [/test/] ->

play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:

1) Could not find a suitable constructor in controllers.TestController. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at controllers.TestController.class(TestController.scala:3)
  while locating controllers.TestController
    for the 4th parameter of router.Routes.<init>(Routes.scala:33)
  at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:137):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$4)

1 error]
    at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:210)
    at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:141)
    at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:296)
    at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:186)
    at akka.stream.impl.fusing.MapAsync$$anon$30.onPush(Ops.scala:1261)
    at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:541)
    at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:423)
    at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:624)
    at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:501)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:599)
Caused by: com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Could not find a suitable constructor in controllers.TestController. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at controllers.TestController.class(TestController.scala:3)
  while locating controllers.TestController
    for the 4th parameter of router.Routes.<init>(Routes.scala:33)
  at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:137):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$4)

1 error
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:543)
    at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:159)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:106)
    at com.google.inject.Guice.createInjector(Guice.java:87)
    at com.google.inject.Guice.createInjector(Guice.java:78)
    at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:200)
    at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:155)
    at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
    at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:189)
    at play.utils.Threads$.withContextClassLoader(Threads.scala:21)

Есть ли простое решение остаться старой школой - не ходя здесь ?

Я признаю, но не полностью понимаю https://www.playframework.com/documentation/2.4.x/Migration24. Я предполагаю, что моя проблема связана с маршрутизацией stati c, удаленной в 2.7 .

Ответы [ 3 ]

3 голосов
/ 23 января 2020

Моя репутация не позволяет мне комментировать ответ от Марио Гали c, но вы можете легко изменить его пример, используя "право" (не test) controllerComponents, которые предоставляются BuiltInComponentsFromContext.

Весь пример будет выглядеть как

class HomeController(override protected val controllerComponents: ControllerComponents)
  extends BaseController {
  def index = Action { Ok("It works!") }
}

class MyApplicationLoader extends ApplicationLoader {
  def load(context: ApplicationLoader.Context): Application = {
    new BuiltInComponentsFromContext(context) {
      lazy val homeController = HomeController(controllerComponents)
      override lazy val router: Router = Routes(httpErrorHandler, homeController)
    }.application
  }
}
2 голосов
/ 21 февраля 2020

Чтобы ответить на комментарий @kujosHeist об эквивалентном примере в Java. Мне кажется, это работает (после Play docs ):

package controllers;

import play.mvc.Controller;
import play.mvc.Result;

public class HomeController extends Controller {
    public Result index() {
        return ok("It works!");
    }
}
public class MyApplicationLoader implements ApplicationLoader {

    @Override
    public Application load(Context context) {
        return new MyComponents(context).application();
    }
}


class MyComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents, AssetsComponents {

    public MyComponents(ApplicationLoader.Context context) {
        super(context);
    }

    @Override
    public Router router() {
        HomeController homeController = new HomeController();
        Assets assets = new Assets(scalaHttpErrorHandler(), assetsMetadata());
        return new router.Routes(scalaHttpErrorHandler(), homeController, assets).asJava();
    }

}

Возможно, вы захотите по-разному определять такие вещи, как, например, обработчики ошибок, но это может быть примерно структура .

1 голос
/ 14 января 2020

Действительно StaticRoutesGenerator было удалено , что необходимо для контроллеров в качестве одноэлементных объектов. Возможно, использование инъекции зависимости от времени компиляции , например, здесь , может приблизить вас к тому, к чему вы привыкли, однако ControllerComponents все равно нужно будет ввести. Технически, можно сделать что-то неуместное, поместив play-test в Compile classpath и использовать stubControllerComponents как это так

class HomeController extends BaseController {
  def index = Action { Ok("It works!") }

  override protected def controllerComponents: ControllerComponents = 
     play.api.test.Helpers.stubControllerComponents()
}

и соответствующий минимальный ApplicationLoader

class MyApplicationLoader extends ApplicationLoader {
  def load(context: ApplicationLoader.Context): Application = {
    new BuiltInComponentsFromContext(context) {
      override def httpFilters: Seq[EssentialFilter] = Nil
      lazy val homeController = new _root_.controllers.HomeController
      lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController)
    }.application
  }
}

Таким образом, HomeController, хотя и является классом, теперь полностью зашито, и в ApplicationLoader. * 1025 создан только один его экземпляр. *

Лично я бы советовал против таких махинаций и считаю, что есть хорошие аргументы , почему Play отошел от одиночных игр, например, тестируемость, thread-safety , et c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...