我应该把我的 ThreadLocal 放在弹簧注入的单例中吗?

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

在我的春季DI tomcat web应用程序中,如果我只是让spring创建一个包含我的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是此类使用该变量的声明。

这种方法的优点和缺点是什么?


答案 1

正如@axtavt提到的,当您谈论Web应用程序时,请求范围的bean通常是ThreadLocals更干净,更优雅的替代品。事实上,在幕后,Spring 使用自己的 ThreadLocal 变量实现了请求范围的 Bean(参见 )。ThreadLocal 和作用域 Bean 都为您提供了相同的基本优势 - 能够访问对象,而无需通过调用堆栈手动传递对象。RequestContextHolder

但是,有一种情况是,ThreadLocal变体会战胜作用域内bean,这是在您希望从Spring的Bean生命周期之外访问对象的情况下。一个很好的例子是在JSP taglib中。Taglib 实例由 servlet 容器控制,而不是 Spring,因此不能参与 Spring 的 IoC 框架,因此不能与请求范围的 Bean(或任何其他 Bean)连接。但是,它们可以访问 ThreadLocal 变量。有一些方法可以解决这个问题,但有时ThreadLocals是最简单的方法。

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

因此,为了建议一种方法,如果你有一个Spring webapp,其中Spring beans想要访问特定于当前请求线程的对象,那么我建议使用请求范围的bean。如果你需要访问那些超出Spring的bean控制范围的对象,那么ThreadLocal可能会更容易,尽管我会尽可能地让事情与作用域bean一起工作。


答案 2

如果您使用Spring,则可以简单地使用请求范围的bean而不是显式s:ThreadLocal

public interface UserName {
    ...
}

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

推荐