【发布时间】:2011-07-25 06:58:21
【问题描述】:
一旦我遇到一个模式,ServletRequest 和响应对象被放入 servlet 的本地 ThreadLocal 变量。 servlet 类也有获取当前请求和响应对象的方法。所以为了得到这些对象,你仍然需要使用 servlet 对象进行操作。
拥有这些ThrealLocal 局部变量有什么意义?
【问题讨论】:
标签: java jakarta-ee servlets thread-local
一旦我遇到一个模式,ServletRequest 和响应对象被放入 servlet 的本地 ThreadLocal 变量。 servlet 类也有获取当前请求和响应对象的方法。所以为了得到这些对象,你仍然需要使用 servlet 对象进行操作。
拥有这些ThrealLocal 局部变量有什么意义?
【问题讨论】:
标签: java jakarta-ee servlets thread-local
我认为更好的情况可能是..
在Service层创建连接对象后放入ThreadLocal然后调用DAO层,从Threadlocal中获取连接对象。
【讨论】:
这真是太可怕了。您应该尽快从 HTTP 请求/会话中获取所需的值。您可以在方法调用或传输对象中传递这些值。您应该努力编写无技术的方法/类。如果您的方法/类从 ThreadLocal 获得了一个 http 请求,那么它就是一个毫无价值的类——它在任何非 http 上下文中都不再有用。
看到人们在 BO(业务对象)或 DAO 中从 ThreadLocal 拉取 http 请求,这让我特别震惊。 HTTP 请求不应出现在应用程序的表示层以外的任何层中。
【讨论】:
当您有一些不是线程安全的对象时,但您想避免同步访问该对象(SimpleDateFormat)。相反,为每个线程提供其自己的对象实例。
您需要非常小心地使用 ThreadLocal 的 remove() 方法清理您 get() 或 set() 的任何 ThreadLocals。
【讨论】:
其他人几乎已经说明了在您介绍的场景中线程局部变量的用途。但是请注意,依赖线程本地的实现是特定于“线程”的,并且当事情从每个请求模型的单个线程移开时会中断。示例是基于事件的服务器,其中少数线程同时用于大量用户请求。
【讨论】:
由于请求和响应对象存储在线程局部变量中,因此您可以线程安全地访问这些对象,而无需将它们作为方法参数传递。
示例 1:没有本地线程
public class MyServlet extends Servlet {
private MyObject myObject = new MyObject();
public void service(ServletRequest request, ServletResponse response) {
myObject.doSomething(request, response);
}
}
public class MyObject {
private MyOtherObject myOtherObject = new MyOtherObject();
public void doSomething(ServletRequest request, ServletResponse response) {
// I do nothing with request/response, but need to accept them in order
// to pass them to myOtherObject
myOtherObject.doSomethingElse(request, response);
}
}
public class MyOtherObject {
public void doSomethingElse(ServletRequest request, ServletResponse response) {
// Do something else with request / response
}
}
示例 2:使用本地线程
public class MyServlet extends Servlet {
private MyObject myObject = new MyObject();
private static ThreadLocal<ServletRequest> currentRequest = new ThreadLocal<ServletRequest>();
public static ServletRequest getCurrentRequest() {
return currentRequest.get();
}
private static ThreadLocal<ServletResponse> currentResponse = new ThreadLocal<ServletResponse>();
public static ServletResponse getCurrentResponse() {
return currentResponse.get();
}
public void service(ServletRequest request, ServletResponse response) {
...
currentRequest.set(request);
currentResponse.set(response);
...
myObject.doSomething();
}
}
public class MyObject {
private MyOtherObject myOtherObject = new MyOtherObject();
public void doSomething() {
// I do not need to know about request / response as I do nothing with them
myOtherObject.doSomethingElse();
}
}
public class MyOtherObject {
public void doSomethingElse() {
// Now I can get the current request / response in a thread safe
// manner and without having to accept them as parameters
ServletRequest request = MyServlet.getCurrentRequest();
ServletResponse response = MyServlet.getCurrentResponse();
// Do something with request / response
}
}
显然,对于简单的 servlet,只传递对象是最简单的事情,但在复杂的场景中,有时拥有一个静态但线程安全的 getter 很有用。
【讨论】:
关键是将请求和响应对象放在本来没有它们的类中(例如,它们不是 servlet)。一个例子是 JSF 托管 bean - 它们的方法不采用 HttpServletRequest 参数,因此您可以通过 FacesContext 获取请求,它们在 ThreadLocal 变量中。
之所以可行,是因为每个请求都由一个单独的线程(由 servlet 容器)处理。所以线程=请求。但是有一个警告——容器倾向于使用线程池。因此,必须始终在 threadlocal 中设置一个新请求,并且最好在之后清理它(例如在 Filter 中)。否则你会得到一些意想不到的行为。
但是您应该在代码中真正避免这种情况。如果您需要请求或响应中的任何内容,请将其作为方法参数传递。否则,您可能会违反层边界(例如,如果您想在服务层中使用请求)
【讨论】:
我不能 100% 确定您曾经遇到过的代码作者的意图是什么,但我想这里的想法是 ServletRequest 实例可以从代码中的任何方法获得而无需传递它作为参数或设置为实例变量。通常ThreadLocal 变量是静态的,并且有一个公开的方法允许静态获取ServletRequest 的实例。例如,您可以使用这种技术在 Struts FromBeans 中轻松访问 ServletRequest。
【讨论】:
它们允许您从项目中的其他类访问 HttpServletRequest 和 HttpServletResponse,而无需将对这些对象的引用传递给其他类。这不是我特别喜欢的模式,因为它往往会将您的 Web 层代码与您的业务逻辑混为一谈,并使单元测试更加困难。
【讨论】: