【问题标题】:Request-scoped property in controllers控制器中的请求范围属性
【发布时间】:2012-05-24 20:37:41
【问题描述】:

我对 Spring 比较陌生,所以有一点对我来说不是很明显。即,控制器是单例的事实。我同意这是一个很好的方法,但这并不能让我实现我以前用其他框架实现的东西。

我构建了一个使用 AJAX 请求的 Web 应用程序。我有以BaseController 作为父级的控制器层次结构,所有其他控制器都对其进行了扩展。

应该向客户端返回响应的控制器操作使用@ResponseBody 注释进行注释,并将序列化的 JSON 字符串返回给客户端。

执行此序列化的方法createJSONResponse()BaseController中实现,这里的主要思想是每个子控制器的操作都使用客户端数据所需的映射Map<String, Object> responseMap,然后访问此映射在BaseController 中并序列化。

我不想在每个操作中都创建responseMap 的新实例,也不想每次都将它作为createJSONResponse() 的参数传递。

我通过将BaseController 中的responseMap 声明为protected 属性来实现这一点。

我不能用 Spring 做到这一点,因为所有控制器都是单例的,我不会在每个请求中都有 responseMap 的新实例。

我不确定将BaseController 请求范围设为一个好主意。

我创建了一个助手类作为请求范围的 bean,并在这个助手中声明了 responseMap。然后我将这个 bean 作为@Autowired 注入BaseController。我还在这个助手中移出了createJSONResponse() 方法。然后从控制器中我将此地图填充为helper.responseMap.add(<somedata>) 并调用helper.createResponseMap()

在这种情况下,responseMapcreateResponseMap() 中变为空,它不包含在控制器操作中填充的数据。不知何故,responseMAp 不是线程安全的,它随着每个异步请求在线程之间移动。

有什么方法可以实现我需要的功能吗?


通过变通方法解决: 通过在每个请求之前实例化 responseMap 并添加到请求拦截器中的 HttpServletRequest 对象来解决问题。然后createJSONResponse() 方法和responseMap 填充方法参考getRequestMap() 方法从HttpServletRequest 中获取responseMap。

附:仍然会很高兴知道更好的解决方案。

【问题讨论】:

  • "我不想在每个动作中创建新的 responseMap 实例,也不想每次都将它作为 createJSONResponse() 的参数传递" --- 为什么不呢?这是最简洁、最容易阅读的设计。
  • 是的。这很干净,但在我看来不是最好的解决方案。我更喜欢 DRY 方法,在控制器的操作中不重复同一行代码。

标签: spring servlets spring-mvc controller


【解决方案1】:

为了做到这一点(线程安全),您将不得不在某些时候为每个请求拥有该映射的一个实例(如果您真的想在单例中注入它,您可以使用范围代理 - - 但你最终会得到完全相同的结果,只是更复杂。

我的建议是使用模板方法并摆脱受保护的属性;只需定义一个抽象方法,该方法将在每个子类中返回填充的地图。

如果您只是想摆脱在所有具体控制器中编写Map<String, Object> responseMap = new HashMap<String, Object>(),您可以在基本控制器中执行此操作(针对每个请求),然后仅使用抽象方法来填充该映射(但实例将每次都是新鲜的)。

这是它在基本控制器中的外观:

public String handleRequest() {
    Map<String, Object> model = new HashMap<String, Object>();
    populateModel(model);
    model.put("success", true);  // as a response to your comment
    return serializeModel(model);
}

protected abstract void populateModel(Map<String, Object> model);

附: 作为旁注(尽管这可能是您问题的实际答案),如果您只是将它们作为类型安全对象返回,Spring 能够以多种格式序列化您的响应。只需在您的类路径中包含 jackson 映射器(以启用 JSON 格式),在您的模型类上添加一些 JAXB 注释并将您的模型作为 MyBean 实例返回。如果您提供正确的 Accept: 标头或在请求映射中定义正确的 produces,Spring 会将其编组为 XML / JSON。

【讨论】:

  • 感谢您的回答,但我不确定这是否是解决方案,因为我需要在请求中在基本控制器和子控制器之间共享地图。我需要在子控制器操作中填充地图,然后在 BaseController 方法中为所有响应(例如 responseMap.add("success", true) )添加一些通用数据。据我了解,使用您提出的方法,我将无法做到这一点。 JSON 响应什么,我已经使用 FlexJSON 正确序列化 Hibernate 实体。
  • populateModel 调用之后,在该地图中添加您想要的任何内容都没有问题。您可以完全控制基本控制器来做任何您想做的事情(如果您不喜欢子类所做的事情,甚至可以将其替换为另一个)。 -- 查看我对代码的编辑
  • 是的,我明白了,但在这种情况下,我必须在子控制器中实现 populateModel 并在每个操作中调用它。我还必须将每个操作中的新地图实例传递给 populateModel 方法,这比直接在 handleRequest() 中直接传递新创建的地图的开销更大。还是我错过了什么?
  • 我不确定我们是否在谈论相同的工作流程。我的理解是你有一个基本控制器可以捕获所有请求(所以任何进来的请求都将由这个handleRequest 处理)。这看起来很像复古弹簧AbstractController 设计。这个假设是错误的吗?
  • 不,这不太对。基本控制器不会捕获所有请求。每个控制器都有自己的操作。
猜你喜欢
  • 2011-09-02
  • 2014-12-02
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多