分类: Java
2014-04-22 00:21:47
一、应用场景
项目采用Spring框架进行设计:MVC+ACEGI+JDBC+AOP。
在项目需求中需要记录用户操作日志,通过Spring AOP设计一个完成日志记录的切面通知。
需要在切面通知中访问当前登录的用户信息,记录登录用户执行的相应操作。
那么,如何在切面通知中访问到当前登录的用户信息呢?有2种方式。
二、实现方式一:前提是在项目中使用了ACEGI框架。
因为用户在登录的时候需要通过Spring Security进行验证,并且验证成功之后会在session中保存用户信息,所以可以直接通过如下方式获取到当前会话的登录用户信息:
UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String user = userDetails.getUsername();
三、实现方式二:通过ThreadLocal来实现。
第一步:用户登录成功后将用户信息保存到session中。
第二步:用户在访问某个action时,从 session中取出用户信息,再保存到与当前线程关联ThreadLocal对象里。(应该是在访问action的filter中从session中取出用户信息保存到ThreadLocal对象中)。
第三步:在 访问某个action时,切面会被织入,由于切面通知与action一定是在一个相同的线程中执行的,并且在访问action时(或者在filter中) 已经将用户信息保存在了ThreadLocal中,所以在执行切面通知时就可以从这个ThreadLocal对象中取出用户信息了。这样就避免了在执行切 面时还要通过参数传递用户信息(这样的设计不好)。
分析:
(1)如果仔细分析一下Spring Security的实现源码,也是这个实现思路。
(2)之所以能够ThreadLocal对象在切面通知中访问到登录用户信息,是因为action和切面通知一定是在一个相同的线程中执行的。
四、ThreadLocal源码分析
// get()方法实现 public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 在ThreadLocal中获取当前线程的threadLocals属性 if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} // 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); // 为当前线程赋值threadLocals属性 }
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLocalMap是ThreadLocal的内部类 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue); // 显然,当前线程的threadlocal属性值一个以ThreadLocal对象为key,要存放的对象为value的map对象 size = 1;
setThreshold(INITIAL_CAPACITY);
}
通过分析ThreadLocal的set(T t)源码可以知道,ThreadLocal为Thread保存的变量值都是保存在线程对象自己的threadLocals属性中。关于ThreadLocal与Thread的关系可以用下图表示:
五、ThreadLocal应用总结
(1)可以通过ThreadLocal对象保存线程本地变量。
(2)ThreadLocal不能实现多线程间的数据共享,即ThreadLocal不能实现多线程同步。多线程同步只能通过synchronized,Lock,wait,notify等方式来实现。
参考博文:
http://www.blogjava.net/jspark/archive/2006/08/01/61165.html
http://blog.csdn.net/qjyong/article/details/2158097