2

我现在已经在几个不同的地方看到了这种模式,但我不确定它的确切用途或为什么需要它。鉴于我在优质项目中见过它,我相信它很有用,但我想了解它而不是盲目地遵循它。我在 Servlet 过滤器和 Struts2 拦截器中特别看到了这种模式(在概念上与过滤器非常相似)。

这是来自 Google Guice (Servlet) 3.0 的示例:

Context previous = localContext.get();

try {
  localContext.set(new Context((HttpServletRequest) servletRequest,
      (HttpServletResponse) servletResponse));

  //dispatch across the servlet pipeline, ensuring web.xml's filterchain is honored
  filterPipeline.dispatch(servletRequest, servletResponse, filterChain);
} finally {
  localContext.set(previous);
}

恢复 finally 块中的值有什么需要或好处?

4

3 回答 3

5

它基本上是一种仅将更改范围限定为try块的方法。无论该块是否成功执行,您都知道,一旦您退出它,您已经将价值恢复到您进入时的值。

于 2011-01-19T21:49:11.597 回答
4

这是一种非常有用的模式,它有效地模拟了 Java 中的非词法作用域。考虑局部上下文本质上是一个全局变量,或者更常见的是线程局部变量。您将全局修改为一个设置值(例如,因为您不想传递给每个称为 HTTP 请求和响应的方法),稍后可以在调用堆栈深处的方法中检索该值。如果你想嵌套这些修改,一个单一的异常可能会让你失控——因此这个结构。不管发生什么(虚拟机崩溃或操作系统强制终止进程),finally 块将在堆栈展开时执行,它的工作是撤消对全局变量的修改,因为功能的嵌套不再需要。

另请参见 Thread#setContextClassLoader()。

于 2011-01-19T21:53:57.603 回答
0

我将尝试举另一个例子,我们发现这种模式很有用。本质上是说,“在 try/catch 块完成使用它们之后,将(通常)全局资源恢复到可预测的状态”。

示例:我们正在使用数据库连接池 => 我们了解创建新连接的成本很高,我们希望重用有限的连接池。因此,某些 servlet 类中的 try 块以 Connection 开头,创建其 Statement 和 ResultSet,将结果写入 ObjectOutputStream 等。这些可能导致 SQLException(有人删除了 db 表中的无用列并且 sql 现在失败)和/或我们在 catch 块中捕获的 IOException(客户端 http 连接在 servlet 仍在写入时中断)。现在,无论是否抛出异常,我们都想关闭数据库连接,以便其他线程可以使用它。这种关闭是在 finally 块中完成的。

希望有帮助,-MS

于 2011-01-19T22:39:14.530 回答