我真的厌倦了这个问题,找不到最好的做法。让我用一个例子来解决这个问题:
我有一个静态方法,如:
public class AppError {
public static void systemError(string errorMsg) { //Like "Can't connect to DB"
HttpServletRequest request = ?????????? "HOW TO ACCESS?";
request.setAttribute("Error", errorMsg);
request.getRequestDispatcher("/error.jsp").forward(request, response(????????));
}
}
正如你所看到的,我需要访问请求和响应对象,我不想通过参数传递HttpServletRequest / Response,因为这意味着将它传递给AppError.systemError调用的地方,那就是这么多地方:读/写文件,cookie和用户操作,数据库操作等......
我以为我找到了一个解决方案,ThreadLocal,我在Filter中使用它并保存了请求和响应对象,但是当我测试时,我通过ThreadLocal.remove()方法释放ThreadLocal变量的Filter.Destroy()方法不被调用它在调试模式下。这意味着该线程可能永远不会被释放。
ServletContextListerner也不是解决方案,因为我找不到访问ServletContext对象中的请求和响应对象的方法。我期待像ServletContext.getRequest()这样的方法,但没有。
我也使用JSF,但JSF的问题是在自定义classess中也无法访问FacesContext。此外,JSF的加载时间晚于侦听器,因此如果我使用FacesContext获取请求和响应对象,则无法在侦听器中使用我的错误系统。
那么这里的最佳做法是什么?我的意思是这是一个明显的问题,如果不解决这个问题我就无法继续前进。
顺便说一句,错误方法就是一个例子,我需要访问其他地方来访问请求和响应对象。
答案 0 :(得分:4)
原则上,正确设置过滤器无法与线程本地存储(TLS)结合使用。
关键的见解不是清除Filter#Destroy()
方法中的TLS,而是清除主过滤链之后的finally子句:
@WebFilter(filterName="requestTLSFilter")
public class RequestTLSFilter extends Filter {
private static final ThreadLocal<ServletRequest> THREAD_LOCAL = new ThreadLocal<ServletRequest>();
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
THREAD_LOCAL.put(request);
chain.doFilter(request, response);
} finally {
THREAD_LOCAL.remove();
}
}
// Other methods
}
关于JSF,FacesContext.getCurrentInstance()
随处可见。 JSF没有隐含的知道谁调用它,但它只能在调用Facelets servlet后调用链中的代码。
还有另外一种选择。如果您的容器具有Java容器Java授权合同(JACC)支持,那么以下内容也会为您提供当前请求:
HttpServletRequest request = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");