Guice 注入实例在 Play 2.5.2 中的请求之间持续存在
Guice Injected Instance persisting between requests in Play 2.5.2
曾几何时,曾经有一位年轻的绅士阅读了一些教程、一些文档、相当多的 Whosebug 问题,甚至可能问了一两个。在他这样做之后,他认为他对事情的运作方式有了一个公平的处理,并着手构建他想要的功能,而且它奏效了!直到邪恶的 出现并尝试使用没有魔法 cookie 的不同浏览器,他才意识到 UserIdentity
一直存在于用户之间......这并不好。
来自戏剧!文档(Guice 文档说的类似)
New instances are created every time a component is needed. If a component is used more than once, then, by default, multiple instances of the component will be created. If you only want a single instance of a component then you need to mark it as a singleton.
我的 IndexController
class 的顶部(我的假设是每个请求调用索引操作一次,因此请求一个新的 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
时,我们可以映射它,以便我们知道身份验证已完成。
这是获取用户方法和为未验证用户调用的 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 = null
在 UserIdentity
class 上,所以它应该是 null
在新实例上。
你说得对,它确实每次都请求一个新实例,但我认为 guice 正在内部重用同一个实例。
我很乐意 post 你的模块定义是肯定的,但我认为你可以摆脱这样的事情以确保每次都创建一个新实例:
bind(classOf[UserIdentity]).toProvider(new Provider[UserIdentity]{
override def get(): UserIdentity = new UserIdentity()
})
感谢 Tompey 的回答,我找对地方了。
故事的寓意是检查您的 Guice Module
。我将 UserIdentity
绑定为 Eager Singleton。因此,通过删除以下行,我能够解决我的问题。
bind(classOf[UserIdentity]).asEagerSingleton()
显然是恶人放在那里
要按需获取实例而不是在实例化 class 时获取实例,您将使用如下提供程序:
class MyController @Inject() (userIdProv: Provider[UserIdentity])
关于您的代码的更多注释:
如果可以避免,请不要注入注入器,而是注入您需要的实际 class(或其提供者)。一方面,您正在创建一种依赖来引导自己。其次,你会在单元测试中注入什么?注射器的模拟?然而,最重要的是,直接注入您所依赖的组件只会让您的代码更清晰。从建模的角度考虑:你有一个东西依赖于另一个东西来完成它的任务——而不是:我有一个东西依赖于 in injector(非常技术性,不具体)。
为什么你的控制器是单例的?首先,播放控制器无论如何都是单例的(文档中没有说明,所以您可能不能永远依赖这种情况),其次,您为什么希望它们是单例?请注意,拥有一个单身人士总是会引入某种状态 - 除非有非常特殊的需要,否则您会希望避免这种情况。
- 不要
Future { user }
。这将创建并执行一个未来。您真正想要的是一个 Future.successful(user)
,它基本上只是用正确的类型包装用户。
曾几何时,曾经有一位年轻的绅士阅读了一些教程、一些文档、相当多的 Whosebug 问题,甚至可能问了一两个。在他这样做之后,他认为他对事情的运作方式有了一个公平的处理,并着手构建他想要的功能,而且它奏效了!直到邪恶的 出现并尝试使用没有魔法 cookie 的不同浏览器,他才意识到 UserIdentity
一直存在于用户之间......这并不好。
来自戏剧!文档(Guice 文档说的类似)
New instances are created every time a component is needed. If a component is used more than once, then, by default, multiple instances of the component will be created. If you only want a single instance of a component then you need to mark it as a singleton.
我的 IndexController
class 的顶部(我的假设是每个请求调用索引操作一次,因此请求一个新的 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
时,我们可以映射它,以便我们知道身份验证已完成。
这是获取用户方法和为未验证用户调用的 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 = null
在 UserIdentity
class 上,所以它应该是 null
在新实例上。
你说得对,它确实每次都请求一个新实例,但我认为 guice 正在内部重用同一个实例。
我很乐意 post 你的模块定义是肯定的,但我认为你可以摆脱这样的事情以确保每次都创建一个新实例:
bind(classOf[UserIdentity]).toProvider(new Provider[UserIdentity]{
override def get(): UserIdentity = new UserIdentity()
})
感谢 Tompey 的回答,我找对地方了。
故事的寓意是检查您的 Guice Module
。我将 UserIdentity
绑定为 Eager Singleton。因此,通过删除以下行,我能够解决我的问题。
bind(classOf[UserIdentity]).asEagerSingleton()
显然是恶人放在那里
要按需获取实例而不是在实例化 class 时获取实例,您将使用如下提供程序:
class MyController @Inject() (userIdProv: Provider[UserIdentity])
关于您的代码的更多注释:
如果可以避免,请不要注入注入器,而是注入您需要的实际 class(或其提供者)。一方面,您正在创建一种依赖来引导自己。其次,你会在单元测试中注入什么?注射器的模拟?然而,最重要的是,直接注入您所依赖的组件只会让您的代码更清晰。从建模的角度考虑:你有一个东西依赖于另一个东西来完成它的任务——而不是:我有一个东西依赖于 in injector(非常技术性,不具体)。
为什么你的控制器是单例的?首先,播放控制器无论如何都是单例的(文档中没有说明,所以您可能不能永远依赖这种情况),其次,您为什么希望它们是单例?请注意,拥有一个单身人士总是会引入某种状态 - 除非有非常特殊的需要,否则您会希望避免这种情况。
- 不要
Future { user }
。这将创建并执行一个未来。您真正想要的是一个Future.successful(user)
,它基本上只是用正确的类型包装用户。