Java ThreadLocal是否比HttpServletRequest.setAttribute(“键”、“值”)更可取?

Java ThreadLocal是否比HttpServletRequest.setAttribute(“键”、“值”)更可取?,java,jakarta-ee,servlets,servlet-filters,thread-local,Java,Jakarta Ee,Servlets,Servlet Filters,Thread Local,servlet规范(参见我前面的问题)保证同一个线程将执行所有过滤器和相关的servlet。鉴于此,如果可以选择使用ThreadLocal(假设您正确清理),则使用HttpServletRequest.setAttribute传递数据没有任何用处。我觉得使用ThreadLocal有两个好处:键入安全性和更好的性能,因为没有使用字符串键或映射(除了可能通过(非字符串)线程id进入线程集合) 请有人确认我是否正确,这样我就可以继续放弃setAttribute?如果您试图为处理HttpServletR

servlet规范(参见我前面的问题)保证同一个线程将执行所有过滤器和相关的servlet。鉴于此,如果可以选择使用
ThreadLocal
(假设您正确清理),则使用
HttpServletRequest.setAttribute
传递数据没有任何用处。我觉得使用
ThreadLocal
有两个好处:键入安全性和更好的性能,因为没有使用字符串键或映射(除了可能通过(非字符串)线程id进入线程集合)


请有人确认我是否正确,这样我就可以继续放弃
setAttribute

如果您试图为处理HttpServletRequest的代码“behind”设置“global”变量,则线程本地工作得更好。如果您使用的是JSP/JSF页面,或者其他从HttpServletRequest读取的web UI组件,那么您将不得不自己从ThreadLocal中提取信息。
对于大多数web编程来说,这两种方法并不等效。

以这种方式使用ThreadLocal意味着您需要依赖某种类型的单例来维护共享状态。最终,这通常被认为是糟糕的设计,因为它有许多缺点,包括难以封装功能、了解依赖关系以及无法交换(或模拟)功能

与在单例中处理不太常见的ThreadLocal变量相比,使用属性或会话的性能开销可能是值得的。然而,这当然取决于您的特定用例和项目。由于难以与servlet共享应用程序状态(很难注入依赖项),因此在servlet中使用单例作为维护应用程序状态的方法有些常见,但这些对象单例对象维护每个用户状态(在EJB之外)是不常见的

如果使用会话或将容器对象设置为属性,则只需通过字符串(即O(1))处理单个获取,然后处理容器类型的单个大小写(该容器类型具有针对所有所需值的特定访问器)。进一步调用代码时,应适当地使用参数,并尽可能避免使用任何类型的全局或单例


最后,在以性能为名考虑一个有点不寻常(尽管很聪明)的解决方案之前,一定要先测试简单的实现,看看它的性能是否足够。与数据库查询和tcp连接建立所花费的20+毫秒相比,此处可能保存的2N很可能微不足道。

我建议不要使用ThreadLocal

我知道你为什么要考虑它,但我认为你陷入了过早的优化陷阱。线程局部变量本质上是全局变量,很少是一个好的设计。节省的时间可以忽略不计。我保证这永远不会成为服务器吞吐量的瓶颈

如果您想开始对某些请求使用异步响应(例如,支持长轮询),那么使用ThreadLocal也可能会给您带来问题,因为线程模型非常不同

ThreadLocal是否比HttpServletRequest.setAttribute(“键”、“值”)更可取

取决于具体的功能需求

例如,JSF将
FacesContext
存储在
ThreadLocal
中。这使您能够访问所有JSF工件,包括由
FacesServlet
执行的代码中的任意位置的“原始”
HttpServletRequest
HttpServletResponse
,例如托管bean。大多数其他基于Java的MVC框架都遵循相同的示例

根据你的评论,


我主要需要将用户和EntityManager对象从用户和数据库过滤器传输到Servlet。我还发现,在后续的代码中经常会意外地需要它们,我很想在Servlet之外使用它们(例如,在doGet调用的嵌套代码中)。我觉得可能有更好的方法来提供更深入的代码建议

对于
User
示例,我假设这是一个会话属性,我宁愿遵循与JSF相同的方法。创建一个
ThreadLocal
,其中
Context
是您的自定义包装类,它包含对当前
HttpServletRequest
的引用,也可能是
HttpServletResponse
的引用,以便您可以在代码中的任何位置访问它们。如有必要,提供方便的方法,直接从
上下文
类获取
用户

至于
EntityManager
示例,您可以采用相同的方法,但我个人不会将其放在相同的
ThreadLocal
中,而是放在不同的方法中。或者,更好的方法是从服务层中的JNDI获取它,这将允许您对事务进行更细粒度的控制。在任何情况下,请务必正确处理提交/关闭。从容器接管持久性和事务管理应该非常小心。我真的要重新考虑一下对使用现有的、设计良好的API/框架(如EJB/JPA)的厌恶,否则您将冒着完全浪费时间的风险重新发明所有已经标准化的API和东西

另见:

请求属性首先用于JSP呈现。我没有使用JSP(“如果可以选择使用
ThreadLocal
”),所以这个问题是关于出于其他原因使用它们。那么您希望在哪里/如何使用这些值?听起来您只是在使用全局ish变量,而不是DI或常规ol参数传递