一、特性表示
mybatis的日志输出具有如下特性:
(1)日志量随程序执行量增加(例如:xml文件中sql报错,那么一定会有初始化、xml加载等信息出现);
(2)日志输出的顺序,是按顺序且隔离的;
二、附上源代码
package org.apache.ibatis.executor;
/**
* @author Clinton Begin
*/
public class ErrorContext {
private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n");
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
//暂存线程上下文
private ErrorContext stored;
private String sql;
... //各个阶段的值存储
private Throwable cause;
private ErrorContext() {
}
public static ErrorContext instance() {
//单例模式,从当前线程获取线程存储变量(一个)
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
public ErrorContext store() {
stored = this;
LOCAL.set(new ErrorContext());
return LOCAL.get();
}
public ErrorContext recall() {
if (stored != null) {
LOCAL.set(stored);
stored = null;
}
return LOCAL.get();
}
public ErrorContext sql(String sql) {
//这种写法是流式编程
this.sql = sql;
return this;
}
public ErrorContext cause(Throwable cause) {
this.cause = cause;
return this;
}
public ErrorContext reset() {
...各个阶段记录初始化
sql = null;
LOCAL.remove();
return this;
}
@Override
public String toString() {
StringBuilder description = new StringBuilder();
...各阶段日志输出
// sql
if (sql != null) {
description.append(LINE_SEPARATOR);
description.append("### SQL: ");
description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
}
// cause
if (cause != null) {
description.append(LINE_SEPARATOR);
description.append("### Cause: ");
description.append(cause.toString());
}
return description.toString();
}
}
此处有几个值得借鉴的地方:
(1)使用ThreadLocal<ErrorContext>类变量可以做到在当前线程执行的任何阶段保证只有一个类对象实例。即使说,只要是出于一个线程,不论走多少层方法栈都没关系,只要使用对应方法将对应阶段的信息记录下来,最终在所有操作完结的使用toString()一下就能将整个过程的信息输出出来。(否则就要将信息存储的类一个接一个方法地以形参方式传入)
(2)每个记录方法都以类对象this返回,可以使用流式编程。简洁明了,可以直接将方法名.出来