我应该将我的ThreadLocals放入弹簧注入的单例中吗?

有几个人(例如在服务器端http://www.theserverside.com/news/thread.tss?thread_id=41473 )建议使用ThreadLocal对象与使用全局变量一样糟糕。 我想如果你把它们变成公共静态变量就行了。 那么问题是很难分辨它的使用位置,改变的位置等等。

在我的春季DI tomcat web-app中,它似乎可以解决这个问题,如果我只是让Spring创建一个包含ThreadLocal的单例对象,然后将该单例注入任何需要它的类中。

所以我的单身人士看起来像这样:

@Component public class Username { private ThreadLocal username; public Username() { username = new ThreadLocal(); } public String getUsername() return username.get(); } public void setUsername(String name) { username.set(name); } } 

可能需要它的类看起来像这样:

 @Service public class addEntryTransaction { @Autowired Username username; public void method() { ... log("Method called by "+username.getUsername()); ... } } 

这仍然具有不必通过许多不关心的层传递用户名的好处,因此保持方法参数更简单。 @Autowired是此类使用该变量的声明。

这种方法有哪些优缺点?

如果你使用Spring,你可以简单地使用一个请求范围的bean而不是显式的ThreadLocal

 public interface UserName { ... } @Component @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) public class UsernameImpl implements UserName { private String username; ... } 

正如@axtavt所提到的,当你谈论Web应用程序时, 请求范围的bean通常是一个更干净,更优雅的ThreadLocals替代品。 实际上,在封面下,Spring使用自己的ThreadLocal变量实现请求范围的bean(请参阅RequestContextHolder )。 ThreadLocal和scoped bean都提供了相同的基本优势 – 访问对象的能力,而无需通过调用堆栈手动传递它。

但是,有一种情况是ThreadLocal变量赢得了scoped bean,这是你想要从Spring的bean生命周期之外访问对象的情况。 一个很好的例子就是在JSP taglib中。 Taglib实例由servlet容器控制,而不是Spring,因此不能参与Spring的IoC框架,因此不能与请求范围的bean(或任何其他bean)连接起来。 但是,它们可以访问ThreadLocal变量。 有很多方法,但有时ThreadLocals是最简单的方法。

ThreadLocal的一个function缺点是它们在数据从线程传递到线程的应用程序中不是很有用(有时,InheritableThreadLocal有助于此,但并非总是如此)。 在这种情况下,Spring的作用域bean也会失败,因为它们是使用ThreadLocal实现的。

所以就一个方法提出建议,如果你有一个Spring webapp,Spring bean需要访问特定于当前请求线程的对象,那么我建议使用请求范围的bean。 如果你需要访问那些超出Spring bean控制范围的对象,那么ThreadLocal可能会更容易,尽管我会尝试尽可能地使用scoped bean。

Spring在内部使用ThreadLocal ,将它们作为基础结构没有任何问题。 但是,您应该避免它们用于业务逻辑

如果你确实需要它们,并且request范围不适合你(出于一些不可预见的原因),那么我建议在内部使用ThreadLocal定义自定义Scope ,从而将其隐藏在业务逻辑中。

当您拥有由JMS,队列和http请求处理的公共代码时,ThreadLocals非常有用。 请求范围值在所有方案中都不起作用。

使用threadLocals时要记住的一件重要事情是服务器容器将重用线程 ,如果设置了某些内容,则是本地变量值,如果您有需要重置的值,请​​使用servletfilter/ AOP代理控制器/ jms侦听器上的对象在调用业务逻辑之前清除值,否则最终可能很难重现调试类型的问题。