I've seen this pattern used in a few different places now, but I'm not sure exactly what it's for or why it's needed. Given that I have seen it in quality projects, I'm sure it's useful, but I'd like to understand it rather than just blindly following it. I've specifically seen this pattern in Servlet Filters and Struts2 Interceptors (very similar in concept to a Filter).
Here's an example from Google Guice (Servlet) 3.0:
Context previous = localContext.get();
try {
localContext.set(new Context((HttpServle开发者_运维百科tRequest) servletRequest,
(HttpServletResponse) servletResponse));
//dispatch across the servlet pipeline, ensuring web.xml's filterchain is honored
filterPipeline.dispatch(servletRequest, servletResponse, filterChain);
} finally {
localContext.set(previous);
}
What is the need or benefit for restoring the value in the finally block?
It's basically a way of scoping the changes only to the try
block. Regardless of whether or not the block successfully executes, you know that once you're out of it, you have restored the value to what it was when you went in.
This is a pattern that is very useful, and it effectively emulates non-lexical scoping in Java. Consider the local context being essentially a global variable, or, more commonly, a thread-local variable. You modify the global to a set value (because e.g. you don't want to pass to each and every method called the HTTP request and response), which can be retrieved later on, in methods deep in the call stack. If you want to nest these modifications, a single exception can throw you out of whack - hence this construct. No matter what happens (short of a VM crash or forced process termination by the OS), that finally block will be executed as the stack unwinds, and its job is to undo the modification to global variables, because the nesting of functionality is no longer needed.
See also Thread#setContextClassLoader().
I will try to give another example where we find this pattern useful. It is essentailly saying, "Restore the (usually) global resources to a predictable state after the try/catch block is done with using them".
The example: We are using DB connection pool => We understand creating new connections is expensive and we'd like to reuse a limited pool of connections. So the try block in some servlet class starts with a Connection, creates its Statement and ResultSet, writes results to an ObjectOutputStream, etc. These can cause SQLException (someone deleted a useless column in the db table and the sql fails now) and/or IOException (client http connection broke while servlet is still writing to it) that we catch in the catch block. Now, whether there is an exception thrown or not, we want to close the db Connection so other threads can use it. This closing is done in the finally block.
Hope that helps, - M.S.
精彩评论