每个Play框架Web请求是否使用新的dependency injection控制器实例进行处理,但那么静态控制器方法呢?

我的问题是关于Play框架中Java控制器的生命周期,如果控制器是有状态实例或静态方法无状态,以及如何在控制器代码中使用dependency injection。

每个Web请求是否由Play控制器类的新实例处理,即控制器是否可以在诸如注入控制器构造函数的服务等字段中存储状态? (在文档中的位置解释了吗?)

自早期版本(如果是,在什么版本?)Play框架是否已更改,关于控制器是有状态实例还是静态方法的无状态控制器?

在哪里可以看到有关在使用有状态控制器时框架如何将服务注入控制器实例的代码示例以及如何将服务注入静态控制器方法的示例?

关于后者,即注入静态方法,我认为要么必须是框架将添加的方法的参数,要么如果不可能,您可能必须使用方法中的服务定位器,例如实例化Guice模块类然后从静态控制器方法中使用“injector.getInstance”。

在以下页面的“dependency injection控制器”部分中可以了解该主题:

https://www.playframework.com/documentation/2.4.x/JavaDependencyInjection

但是,它没有用代码显示如何将服务实际注入到控制器实例中(但可能与其他“组件”相同,即使用@Inject注释)当然它当前没有显示如何使用静态控制器方法的DI 。

我对这些事情感到困惑,因为我没有发现文档清楚我的问题,而且我还在Play书(从2013年)中读到控制器方法应该被编程为无状态,控制器方法应该是静态的。

但是,当现在使用激活器生成具有最新Play版本(2.4.6)的Java Play应用程序时,我可以看到生成的Controller方法(Application.index)不是静态的。 此外,在以下文档页面中,控制器方法不是静态的: https : //www.playframework.com/documentation/2.4.x/JavaActions

这是令人困惑的,因为了解每个请求是否由Controller实例处理是非常基础的(即,如果可以使用状态)我认为这应该在关于控制器/动作的页面上比当前更好地记录。文档(上面链接的页面)没有解释它。

有关dependency injection的文档在“dependency injection控制器”部分提到“静态路由生成器”时涉及静态和非静态方法的主题,但我认为应该更好地解释包括代码示例。

如果Play团队中有人正在阅读此问题,那么请在上面的链接页面中添加一些信息,例如请提及(如果我的理解是正确的)在以前版本的Play中控制器方法是静态的,对于那些版本你永远不应该在字段中存储状态,但是在更高版本中(从版本x开始?),每个请求都由控制器的实例处理,因此可以使用状态(例如框架注入的构造函数参数)。

还请提供有关静态控制器方法使用的注入和注入有状态控制器实例的代码示例,每个请求只有一个实例。

dependency injection页面中的“组件生命周期”部分仅提到“组件”,但我认为它也应该明确控制器生命周期及其注入,因为它是一种基本且重要的知识,可以清楚地向所有开发人员进行通信以避免错误由于对有状态与否的误解而引起的。

每个Web请求是否由Play控制器类的新实例处理,即控制器是否可以在诸如注入控制器构造函数的服务等字段中存储状态? (在文档中的位置解释了吗?)

据我所知,控制器默认是单例对象 。 这没有明确记录,但暗示控制器实例被重用。 请参阅Playframework 2.4的迁移指南 :

注入的路由生成器也支持路由上的@运算符,但它的含义略有不同(因为所有内容都被注入),如果你在控制器前加上@,而不是直接注入该控制器,那么该控制器的JSR 330 Provider将会被注射 例如,这可以用于消除循环依赖性问题, 或者如果您希望每个请求实例化一个新操作

另外,请查看James Roper (播放核心提交者)对控制器是否为单身人士所做的推荐 :

不是真的 – 如果使用Guice,每次将控制器注入某个东西时,默认情况下都会创建一个新实例。 也就是说, 路由器是一个单例,因此通过关联,它调用的控制器是单例 。 但是如果你在其他地方注入一个控制器,它将被新实例化为该组件。

这表明默认情况下是在响应请求时重用控制器实例,如果您希望每个请求执行一个新操作,则需要使用迁移指南中描述的语法。 但是……因为我更倾向于certificate和尝试事物而不仅仅是相信,我创建了一个简单的控制器来检查该语句:

package controllers import play.api._ import play.api.mvc._ class Application extends Controller { def index = Action { println(this) Ok(views.html.index("Your new application is ready.")) } } 

对此操作执行多个请求会为所有请求打印相同的对象标识 。 但是,如果我在我的路线上使用@运算符,我会开始为每个请求获得不同的身份 。 所以,是的, 默认情况下控制器是(有点)单例

自早期版本(如果是,在什么版本?)Play框架是否已更改,关于控制器是有状态实例还是静态方法的无状态控制器?

默认情况下,Play总是提倡无状态控制器,正如您在项目主页上看到的那样 :

Play基于轻量级, 无状态 ,Web友好的架构。

那并没有改变。 因此,您不应该使用控制器的字段/属性来保持随时间/请求而变化的数据。 相反,只需使用控制器的字段/属性来保持对无状态的其他组件/服务的引用。

在哪里可以看到有关在使用有状态控制器时框架如何将服务注入控制器实例的代码示例以及如何将服务注入静态控制器方法的示例?

关于代码示例, Activator模板存储库是最佳选择。 以下是一些在控制器级别使用dependency injection的示例:

  1. https://github.com/adrianhurt/play-api-rest-seed
  2. https://github.com/knoldus/playing-reactive-mongo
  3. https://github.com/KyleU/boilerplay

不支持使用静态方法的dependency injection,这就是为什么Playframework仍然提供旧的api与静态方法一起使用的原因。 这里的经验法则是:在DI和静态方法之间进行选择。 尝试使用这两者只会给您的应用程序带来复杂性。

好的,谢谢marcospereira。 我现在还确认您确实为每个请求获得了控制器的不同实例(可以在控制器方法中打印/记录的不同的toString值)。

对于那些感兴趣的人,解决方案(为每个请求获取控制器类的不同实例)将使用例如以下内容:

 GET / @controllers.Application.index() 

而不是以下:

 GET / controllers.Application.index() 

在文件“conf / routes”AND中也使用以下内容:

 routesGenerator := InjectedRoutesGenerator 

而不是以下:

 routesGenerator := StaticRoutesGenerator 

在文件“build.sbt”中

关于Play有“无状态”架构的说法:也许我错了,但就我理解的术语而言,“无状态”意味着Web服务器不会在请求之间存储任何状态? “无状态”一词并不意味着控制器实例不能使用字段,例如注入构造函数。 如果注入的对象存储为控制器中的字段,则该字段是控制器的“状态”。 因此,即使您使用“InjectedRoutesGenerator”和“@”前缀来获取“有状态”控制器实例,注入“状态”的只会存储在一个请求中,因此您仍然可以说框架本身是“无状态”的,因为服务器不会在多个请求之间存储任何状态。 如果我误解了Play无国籍的话,请纠正我。