【问题标题】:Guice Injected Instance persisting between requests in Play 2.5.2Guice Injected Instance 在 Play 2.5.2 中的请求之间持续存在
【发布时间】:2016-09-05 00:59:09
【问题描述】:

从前,有一位年轻的绅士阅读了一些教程、一些文档、一些 StackOverflow 问题,甚至可能会问一两个问题。在他这样做之后,他认为他对事情的运作方式有了一个公平的把握,并着手构建他想要的功能,并且它奏效了!直到邪恶的女巫出现并尝试使用没有魔法 cookie 的不同浏览器时,他意识到 UserIdentity 已在用户之间持续存在......这并不好。

来自戏剧! docs(Guice 文档说类似)

每次需要组件时都会创建新实例。如果一个组件被多次使用,那么默认情况下,将创建该组件的多个实例。如果您只想要组件的单个实例,则需要将其标记为单例。

我的IndexController 类的顶部(我的假设是每个请求调用一次索引操作,因此请求一个新的UserIdentity——这是错误的吗?)

@Singleton
class IndexController @Inject() (contentModel: ContentModel, site: Site, injector: Injector) extends Controller {

  def index(url: String, page: Int = 1, sort: String, dir: Int) = Action.async { implicit request =>
    implicit val queryString : QueryString = request.queryString
    val userIdentityClass = injector.getInstance(classOf[UserIdentity])
    implicit val userIdentity : Future[UserIdentity] = userIdentityClass.getUserIdentity
    doContent(url, page, sort, dir) fallbackTo doAlias(url) fallbackTo do404(url)
  }

userIdentity 然后隐式传递到机器的内部。

getUserIdentity 方法调用进行身份验证的getUser 方法,并设置user : UserModel 属性。这一切都在未来传递,因此当我们稍后在应用程序中使用 userIdentity 时,我们可以映射它,以便我们知道身份验证已完成。

这是 get user 方法和为未经身份验证的用户调用的 println

private def getUser(implicit request:Request[AnyContent]) : Future[UserModel] = {
  if (user != null) {
    println("User already set")
    Future { user }
  } else {
    //Go and find the user from cookie/some other auth stuff

注意 var user : UserModel = nullUserIdentity 类上,所以它应该是 null 在新实例上。

【问题讨论】:

    标签: scala playframework guice implicit playframework-2.5


    【解决方案1】:

    要按需而不是在实例化类时获取实例,您将使用如下提供者:

    class MyController @Inject() (userIdProv: Provider[UserIdentity])
    

    关于您的代码的更多注释:

    • 如果可以避免,请不要注入注入器,而是注入您需要的实际类(或其提供者)。一方面,您正在创建对 guice 本身的依赖。其次,你会在单元测试中注入什么?注射器的模拟?然而,最重要的是,直接注入您所依赖的组件只会使您的代码更清晰。从建模的角度考虑:你有一个依赖于另一个东西来完成任务的东西——而不是:我有一个依赖于注入器的东西(非常技术性,不具体)。

    • 为什么你的控制器是单例的?首先,播放控制器无论如何都是单例的(文档中没有说明,所以你可能不能永远依赖这种情况)其次,你为什么希望它们成为单例?请注意,单例总是会引入某种状态——除非有非常特殊的需要,否则您会希望避免这种情况。

    • 不要做Future { user }。这将创造并执行一个未来。您真正想要的是Future.successful(user),它基本上只是用正确的类型包装用户。

    【讨论】:

    • 谢谢。至于为什么我的控制器是单例的,我相信我遵循了播放示例项目甚至介绍视频。我有点认为这就是玩的方式!需要它。
    【解决方案2】:

    你是对的,它每次都请求一个新实例,但我认为 guice 在内部重用同一个实例。

    我会很高兴发布您的模块定义以确保,但我认为您可能能够摆脱这样的事情来确保每次都创建一个新实例:

    bind(classOf[UserIdentity]).toProvider(new Provider[UserIdentity]{
      override def get(): UserIdentity = new UserIdentity()
    })
    

    【讨论】:

    • 您好汤佩,感谢您的回答。它不会起作用,因为我需要注入我的UserIdentity。但是它确实让我查看了我的模块文件并找到了这条流氓行bind(classOf[UserIdentity]).asEagerSingleton()
    【解决方案3】:

    感谢汤佩的回答,我找到了正确的地方。

    这个故事的寓意是检查你的 Guice Module。我将 UserIdentity 绑定为 Eager Singleton。因此,通过删除以下行,我能够解决我的问题。

    bind(classOf[UserIdentity]).asEagerSingleton()
    

    显然是邪恶的女巫把它放在那里的。

    【讨论】:

    • 不过,您不应该注射注射器。根据 Tompey 的建议,通过提供者获取 UserIdentity 的实例(不确定您是否需要该绑定,或者只需在控制器中请求提供者“uiProv:Provider[UserIdentity]”即可)
    • @rethab,其他地方告诉我注射注射器。因为UserIdentity有以下参数,都是注入class UserIdentity @Inject() (userModel: UserModel, ws: WSClient, site: config.Site, cacheApi: CacheApi) {
    • @rethab 你能补充一个答案吗?并说为什么注射注射器是个坏主意?
    • 我已经添加了答案。
    猜你喜欢
    • 1970-01-01
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2012-04-17
    • 2014-02-12
    相关资源
    最近更新 更多