相信很多做分布式web开发的都封装过这样的一个工具用来管理当前登录的用户。
在拦截器里面把用户set进来
在controller 里面get 出来使用
而且都是基于ThreadLocal 这个模板类来封装的, 出于好奇跟踪进源码一探究竟
注意:由于我所在公司开发使用jdk1.6开发。以下源码为jdk1.6源码,可能与您使用的不一致
这里简单写个例子
public class BaseLoginContext<T extends BaseLoginContext> {
private final static ThreadLocal<BaseLoginContext> holder = new ThreadLocal<BaseLoginContext>();
private final static Log log = LogFactory.getLog(BaseLoginContext.class);
public static void remove() {
holder.remove();
}
public static BaseLoginContext get() {
return holder.get();
}
protected static void set(BaseLoginContext context) {
holder.set(context);
}
}
public class LoginContext extends
BaseLoginContext<LoginContext> {
private User user = new User();
public static Long getCurrentUserId() {
if (getCurrentUser() == null) {
return null;
}
return getCurrentUser().getPk();
}
public static boolean isLogin() {
if (getCurrentUser() == null) {
return false;
}
return getCurrentUserId() > 0;
}
public static UnionUser getCurrentUser() {
final UnionMasterLoginContext loginContext = (UnionMasterLoginContext) get();
if (loginContext == null) {
return null;
}
User user = loginContext.getUser();
return user;
}
public User getUser() {
return user;
}
static public void addCurrentUser(User user) {
LoginContext context = (LoginContext) get();
if (context == null) {
set(new LoginContext());
context = (LoginContext) get();
}
context.setUser(user);
}
public void setUser(UnionUser user) {
if (user == null) {
user = this.user;
}
}
先看一下ThreadLocal的set
/**
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
获取当前线程 然后get的到一个ThreadLocalMap,如果不为空就就把当前类(this)作为key要设置的值设置为值。 (这里提一下获取当前线程Thread.currentThread()是java 虚拟机原生实现的源码里可以看到public static native Thread currentThread();使用了native 标记,这表示这个方法我们主管用,原生实现的方法,喜欢的同学可以去找虚拟机源码或者查找jvm规范)
要注意这里key传的是this 当前类对象
至于为什么 我们下面讲述
如果ThreadLocalMap 为空 就调用createMap 创建一个参数为当前线程和要放入的参数。
下面我们先看看 getMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这里直接返回线程的成员变量threadLocals,在Thread中定义了ThreadLocal.ThreadLocalMap threadLocals = null;
这里看到 ThreadLocalMap 是ThreadLocal的内部类,这个我们可以在ThreadLocal中找到,有点类似与Map 的一个东西,用法也很类似,就理解成一个map也可以 ,这块就不详细说明了, 那么现在我们来看一下createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这里给当前线程创建一个ThreadLocalMap ,
然后和set 里面一样把当前类(this)作为key要设置的值设置为值。
现在我们看一下 ThreadLocal的get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
同样也是获取当前线程,然后拿到当前线程的ThreadLocalMap 然后取出key为this 也就是本对象的值。
如果 map 为空 获取一个初始化的值 (其实就是null)
我们可以看一下setInitialValue实现
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
调用initialValue获取一个默认值 ,其实源码可以看到initialValue什么都没干直接返回null。然后的流程和set 一样 (真好奇这里获取默认值后为什么不直接调用set ,估计是不利于以后改动。万一set变化会影响到)
remove就没什么可说的了很简单
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
一样的逻辑获取当前线程 ,获取ThreadLocalMap 然后移除本身就可以了;
这里我们讲述一下key传的是this 当前类对象
Thread 中有成员变量ThreadLocalMap threadLocals;是实现ThreadLocal的基础。threadLocals 在每一个线程中都属于线程独自的。
当我们实现一个ThreadLocal ,threadLocals 中就会增加一个键值对。key 就是我们实例化的ThreadLocal,value 就是我们要存储到本地线程的值,key 传this 就把我们实例化的ThreadLocal对象作为键存在threadLocals 中,这样我们实现多个ThreadLocal 的时候 threadLocals 都可以支持,也都能保证ThreadLocal 取到的是自己存放的值。
(刚开始写博客,文字表达能力有限,正在努力学习,请多多见谅)