有些人(例如在服务器端http://www.theserverside.com/news/thread.tss?thread_id=41473)表示使用ThreadLocal对象与使用全局变量一样糟糕。 我想如果你把它们变成公共静态变量就行了。 问题在于,很难分辨它的使用位置,改变的位置等等。
在我的春天DI tomcat web-app中,它似乎可以解决这个问题,如果我只是为了创建一个包含我的ThreadLocal的单例对象,然后将该单例注入任何需要它的类中。
所以我的单身人士看起来像这样:
@Component
public class Username {
private ThreadLocal<String> username;
public Username() {
username = new ThreadLocal<String>();
}
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是该类使用该变量的声明。
这种方法有哪些优点和缺点?
答案 0 :(得分:19)
正如@axtavt所提到的,当你谈论Web应用程序时,request-scoped beans通常是一个更清晰,更优雅的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的一个功能缺点是它们在数据从线程传递到线程的应用程序中不是很有用(有时,InheritableThreadLocal有助于此,但并非总是如此)。在这种情况下,Spring的作用域bean也会失败,因为它们是使用ThreadLocal实现的。
所以建议一个方法,如果你有一个Spring webapp,Spring bean需要访问特定于当前请求线程的对象,那么我建议使用请求范围的bean。如果你需要访问那些超出Spring bean控制范围的对象,那么ThreadLocal可能会更容易,尽管我会尝试尽可能地使用scoped bean。
答案 1 :(得分:11)
如果您使用Spring,则只需使用请求范围的bean而不是显式的ThreadLocal
:
public interface UserName {
...
}
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public class UsernameImpl implements UserName {
private String username;
...
}
答案 2 :(得分:6)
Spring在内部使用ThreadLocal
,将作为基础架构并没有错。但是,您应该为业务逻辑避免使用它们。
如果您真的需要它们,并且request
范围不适合您(出于某些不可预见的原因),那么我建议您在内部使用Scope
定义自定义ThreadLocal
因此将其隐藏在业务逻辑中。
答案 3 :(得分:1)
当您拥有由JMS,队列和http请求处理的公共代码时,ThreadLocals非常有用。请求范围值在所有方案中都不起作用。
使用threadLocals时要记住的一件重要事情是服务器容器将重新使用线程,如果设置了某些内容,则局部变量值也是如此,如果您有需要的值要重置,请在控制器/ jms侦听器上使用servlet过滤器/ AOP代理对象,以便在调用业务逻辑之前清除这些值,否则最终可能会很难调试以重现类型的问题。