在项目日志拦截器中使用了threadlocal,在记录日志时,
private static final ThreadLocal<Long> startTimeThreadLocal =
new NamedThreadLocal<Long>("ThreadLocal StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (logger.isDebugEnabled()){
long beginTime = System.currentTimeMillis();//1、开始时间
startTimeThreadLocal.set(beginTime); //线程绑定变量(该数据只有当前请求的线程可见)
logger.debug("开始计时: {} URI: {}", new SimpleDateFormat("hh:mm:ss.SSS")
.format(beginTime), request.getRequestURI());
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 打印JVM信息。
if (logger.isDebugEnabled()){
long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
long endTime = System.currentTimeMillis(); //2、结束时间
logger.debug("计时结束:{} 耗时:{} URI: {} 最大内存: {}m 已分配内存: {}m 已分配内存中的剩余空间: {}m 最大可用内存: {}m",
new SimpleDateFormat("hh:mm:ss.SSS").format(endTime), DateUtil.formatDateTime(endTime - beginTime),
request.getRequestURI(), Runtime.getRuntime().maxMemory()/1024/1024, Runtime.getRuntime().totalMemory()/1024/1024, Runtime.getRuntime().freeMemory()/1024/1024,
(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024);
//删除线程变量中的数据,防止内存泄漏
startTimeThreadLocal.remove();
}
由于过滤器filter的拦截是的没有直接访问
preHandle方法,所以
ThreadLocal没有set值,就直接访问
afterCompletion从ThreadLocal中get值,导致ThreadLocal底层使用的map为空,执行initialValue方法并抛出NullPointException,根据jdk文档推荐做法Method Detail
-
initialValue
protected T initialValue()
Returns the current thread's "initial value" for this thread-local variable. This method will be invoked the first time a thread accesses the variable with theget()
method, unless the thread previously invoked theset(T)
method, in which case theinitialValue
method will not be invoked for the thread. Normally, this method is invoked at most once per thread, but it may be invoked again in case of subsequent invocations ofremove()
followed byget()
.This implementation simply returns
null
; if the programmer desires thread-local variables to have an initial value other thannull
,ThreadLocal
must be subclassed, and this method overridden. Typically, an anonymous inner class will be used.-
Returns:
-
the initial
value for this thread-local - 采取如下解决方案,事实证明,该initialValue确实执行,并不在抛出NullPointException,但另外在项目中就存在为什么被过滤器拦截后执行发生变化,不是先执行prehandle再执行aftercompletion,而是出现反复执行 aftercompletion的问题待解决
-
private static final ThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("ThreadLocal StartTime"){ protected Long initialValue() { Logger logger = LoggerFactory.getLogger(getClass()); logger.info("ThreadLocal StartTime initialValue"); long beginTime = System.currentTimeMillis(); return beginTime; }; };
-
the initial