在 Grails 控制器中包含具有会话范围的服务?

Include Service with Session Scope in Grails Controller?

使用 Grails 2.4.5 我有以下服务:

class CartService {

    static scope = 'session'
    static proxy = true

    def items = []

    def getItemCount() {
        items.size()
    }

}

我想在控制器中使用此服务:

class CartController {

    def cartService // unique per session

    def addItem = {
        cartService.items << new CartItem(product: Product.get(params.id))
    }

}

我尝试了 Grails scoped-proxy plugin 的 0.3 版。但是我收到以下错误:

Error creating bean with name 'cartService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    Line | Method
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread
Caused by BeanCreationException: Error creating bean with name 'cartService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread

如何在 Grails 控制器中包含具有会话范围的服务?

看起来您可能发现了 Grails 2 插件的一两个问题(也已报告)。4.x。如果是这样的话,可以参考插件作者的一篇博客post:Scoped Services & Proxies in Grails.

在那里,他展示了如何在没有插件的情况下做到这一点。我试了一下,它在 2.4.5 中有效。

resources.groovy:

import org.springframework.aop.scope.ScopedProxyFactoryBean

beans = {
    cartServiceProxy(ScopedProxyFactoryBean) {
        targetBeanName = 'cartService'
        proxyTargetClass = true
    }
}

代理是手动完成的,因此您可以删除 static proxy

class CartService {
    static scope = 'session'
    def items = []

    def getItemCount() {
        items.size()
    }
}

控制器然后引用代理

class CartController {
    def cartServiceProxy 
    ... 
}

请注意一点(至少对我而言)令人困惑的一点是 Controller Documentation for 2.4.x says the default scope is prototype but in the section describing Scoped Services 它表示从 2.3.x 开始默认控制器范围是 singleton.

希望这对您有所帮助。

更新:澄清一下,在 Grails 2.3 之后,默认控制器范围仍然是 prototype,但是当 grails 生成新应用程序时,会生成将默认控制器范围设置为 singleton 的配置(如果可能的话,这真的是你应该拍摄的,就像伯特在下面说的那样)。

在 Config.groovy

中生成配置
// The default scope for controllers. May be prototype, session or singleton.
// If unspecified, controllers are prototype scoped.
grails.controllers.defaultScope = 'singleton'