我知道Grails的早期版本使用了控制器的原型范围,因为当时的操作都是闭包。
我知道当前的版本文档为使用方法作为操作的控制器推荐单例范围控制器。
从以下帖子看,方法和单身范围似乎更受欢迎或推荐,但不清楚原因。
ttp://grails.1312388.n4.nabble.com/Default-scope-for-controllers-doc-td4657986.html
我们有一个大型项目,它使用原型范围控制器,并将操作作为方法。更改为建议的控制器范围涉及风险和重新测试,并从现有控制器中删除任何非单件友好状态。
我想知道为什么Grails建议使用单一范围作为操作控制器?是因为它更常见,更类似于Spring MVC,避免混淆,还是有机会提高性能,或者是什么? 如果我切换到单控制器,我会获得什么?如果我不进行切换会有什么代价?
答案 0 :(得分:20)
我对Rails并没有太多帮助,但是(至少在我玩的版本中,现在情况可能不同)控制器是模型,包含要由视图呈现的数据。在请求处理期间,您将值存储在控制器实例字段中,然后再处理查看渲染器的处理。因此,为每个请求创建一个新的控制器实例是有意义的,因此它们是不同的。
Grails的灵感来自于Rails并使用了它的一些约定,最着名的是约会配置。但是,使用控制器作为模型的能力也被添加为一个特征,虽然它没有很好的文档记录,我怀疑很多人使用它。
使用GSP渲染响应时控制器动作的典型工作方式(与转发或重定向相反,或直接在控制器中渲染,例如使用render foo as JSON
)是返回带有一个或多个的Map来自操作的键/值对,通常省略return
关键字,因为它在Groovy中是可选的:
class MyController {
def someAction() {
def theUser = ...
def theOtherObject = ...
[user: theUser, other: theOtherObject]
}
}
此处模型映射有两个条目,一个由user
键控,另一个由other
键控,这些条目将是GSP中用于访问数据的变量名称。
但大多数人不知道的是,你也可以这样做:
class MyController {
def user
def other
def someAction() {
user = ...
other = ...
}
}
在这种情况下,模型映射不会从操作中返回,因此Grails将从控制器类的所有属性填充模型,在这种情况下,相同的GSP将适用于这两种方法,因为变量名称在第二种方法与第一种方法中的地图键相同。
制作控制器单例的选项在2.0中添加(技术上1.4在重命名为2.0之前,参见this JIRA issue),除了保留对闭包的支持之外,我们还添加了对方法的支持。最初的假设是使用闭包会启用一些有趣的功能,但这种情况从未发生过。使用方法更自然,因为你可以在子类中覆盖它们,而不像只是类范围字段的闭包。
作为2.0返工的一部分,我们删除了Rails启发的功能,因为它基本上没有记录,因此对使用该功能的少数奇怪应用程序的影响不会很大。我不记得有人抱怨过丢失这个功能。
虽然控制器类通常很容易被垃圾收集,并且每个请求创建一个实例并不会影响很多,但在控制器中很少需要每个请求状态,因此单例通常更有意义。默认的原型范围是为了向后兼容,但是使用Config.groovy属性更改默认值(并且create-app
脚本生成的文件会这样做)。
虽然每个请求都会获得新的请求和响应,并且如果使用会话,则每个用户都将拥有自己的,但这些不是控制器的实例字段。它们看起来像是因为我们可以在操作中访问request
,response
,session
,params
等,但这些实际上是{的属性访问形式{1}},getRequest()
,getResponse()
和getSession()
方法在编译期间通过AST转换混合到所有控制器中。这些方法不是作为类字段而是通过ThreadLocals访问它们的对象,因此存在每请求状态但它没有存储在控制器实例中。
我不记得是否有很多基准测试来比较使用方法和闭包,但Lari Hotari可能做了一些。如果存在差异,则可能并不重要。您可以在自己的应用程序中通过转换一个或几个控制器并在测试之前和之后进行测试。如果两种方法之间的性能,缩放和内存差异不大,那么您可能会安全地保持原型分数和/或闭包。如果存在差异并且您的控制器中没有实例字段,那么它可能值得转换为单例和方法。
如果您有实例字段,它们可能会转换为请求属性 - getParams()
是request.foo = 42
的元类快捷方式,因此您可以安全地存储每个请求数据。