为什么 Grails 推荐将动作作为方法的控制器使用单例作用域?

Why does Grails recommend singleton scope for controllers with actions as methods?

我知道 Grails 的早期版本使用控制器的原型作用域,因为当时的操作都是闭包。

我知道当前版本文档建议将方法用作操作的控制器使用单例范围控制器。

从下面的post来看,方法和单例范围似乎更可取或推荐,但不清楚为什么。
ttp://grails.1312388.n4.nabble.com/Default-scope-for-controllers-doc-td4657986.html

我们有一个大型项目,它使用原型范围的控制器,并将操作作为方法。更改为推荐的控制器范围涉及风险和重新测试,并从现有控制器中删除任何非单例友好状态。

我想知道为什么 Grails 推荐单例作用域 作为动作控制器的方法?仅仅是因为它更常见且类似于 Spring MVC,并且避免了混淆,还是有提高性能的机会,还是什么? 如果我切换到单例控制器我会得到什么?如果我不切换会有什么代价?

我对Rails的工作不多,但是(至少在我玩过的版本中,现在情况可能有所不同)控制器是模型,包含要由视图呈现的数据.在请求处理期间,您将值存储在控制器实例字段中,然后再将处理交给视图渲染器。因此,为每个请求创建一个新的控制器实例是有意义的,因此它们是不同的。

Grails 受到 Rails 的启发并使用了它的几个约定,最著名的是约定优于配置。但是使用控制器作为模型的能力也被添加为一个特性,尽管它没有很好的记录并且我怀疑很多人使用它。

当使用 GSP 呈现响应(而不是转发或重定向,或直接在控制器中呈现,例如使用 render foo as JSON)时,控制器操作的典型工作方式是 return具有来自操作的一对或多对 key/value 的 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 = ...
   }
}

在这种情况下,模型映射不是从操作中 return 编辑的,因此 Grails 将从控制器的所有属性填充模型 class,在这种情况下,相同的 GSP 将适用于这两种方法,因为第二种方法中的变量名称与第一种方法中的映射键相同。

在 2.0 中添加了使控制器成为单例的选项(技术上是 1.4,然后才重命名为 2.0,请参阅 this JIRA issue),除了保留对闭包的支持之外,我们还添加了对作为操作的方法的支持。最初的假设是使用闭包会启用一些有趣的功能,但那从未发生过。使用方法更自然,因为您可以在 subclasses 中覆盖它们,这与闭包不同,闭包只是 class-scope 字段。

作为 2.0 返工的一部分,我们删除了受 Rails 启发的功能,因为它基本上没有记录,因此对使用该功能的少数奇怪应用程序的影响不会很大。我不记得有人抱怨过该功能的丢失。

虽然控制器 classes 通常很容易被垃圾回收并且为每个请求创建一个实例不会有太大影响,但很少需要控制器中的每个请求状态,因此单例通常更有意义。保留默认原型作用域是为了向后兼容,但很容易使用 Config.groovy 属性 更改默认值(由 create-app 脚本生成的文件会执行此操作)。

虽然每个请求确实得到一个新的请求和响应,如果使用会话,每个用户将有自己的,但那些不是控制器的实例字段。它们看起来像它们是因为我们可以在操作内部访问 requestresponsesessionparams 等,但这些实际上是 属性 访问getRequest()getResponse()getSession()getParams() 方法的形式,在编译期间通过 AST 转换混合到所有控制器中。这些方法不是通过 class 字段而是通过 ThreadLocals 访问它们的对象,因此每个请求都有状态,但它没有存储在控制器实例中。

我不记得是否有很多基准测试方法来比较使用方法和闭包,但 Lari Hotari 可能做了一些。如果有差异,那可能并不显着。您可以通过仅转换一个或几个控制器并进行前后测试来在您自己的应用程序中对此进行测试。如果两种方法之间的性能、缩放和内存差异并不显着,您可能会安全地使用原型得分 and/or 闭包。如果存在差异并且您的控制器中没有实例字段,那么转换为单例和方法可能是值得的。

如果您确实有实例字段,它们可能会被转换为请求属性 - request.foo = 42request.setAttribute('foo', 42) 的元 class 快捷方式,因此您可以安全地存储每个请求的数据相反。