MyBatis 日志设计思想

整体架构用了门面模式,对外暴露 SqlSession 接口

日志模块,采用了适配器模式

如果需要接入多个第三方 SDK 来满足自己的业务需求,但是 SDK 的接口定义与你的业务不兼容,又不能修改第三方 SDK 的代码,可以用适配器设计模式解决

MyBatis 定义了自己 Log 接口,为大部分主流日志框架都实现了 Adapter 适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Log {

boolean isDebugEnabled();

boolean isTraceEnabled();

void error(String s, Throwable e);

void error(String s);

void debug(String s);

void trace(String s);

void warn(String s);

}

定义了四种 Log 级别,以 Slf4j 实现为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class Slf4jImpl implements Log {

private Log log;

public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz);

if (logger instanceof LocationAwareLogger) {
try {
// check for slf4j >= 1.6 method signature
logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class,
Throwable.class);
log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
return;
} catch (SecurityException | NoSuchMethodException e) {
// fail-back to Slf4jLoggerImpl
}
}

// Logger is not LocationAwareLogger or slf4j version < 1.6
log = new Slf4jLoggerImpl(logger);
}

@Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}

@Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
}

@Override
public void error(String s, Throwable e) {
log.error(s, e);
}

@Override
public void error(String s) {
log.error(s);
}

@Override
public void debug(String s) {
log.debug(s);
}

@Override
public void trace(String s) {
log.trace(s);
}

@Override
public void warn(String s) {
log.warn(s);
}

}

同时定义了一个 LogFactory 类,日志的实例是从这里获取的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class LogFactory {

/**
* Marker to be used by logging implementations that support markers.
*/
public static final String MARKER = "MYBATIS";

private static Constructor<? extends Log> logConstructor;

static {
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}

private LogFactory() {
// disable construction
}
//...
}

通过静态代码块定义了类加载的顺序,slf4j -> commons-logging -> log4j2 -> log4j -> jdk-logging -> no-logging

如果项目中有对应的依赖,则通过反射加载对应的日志接口

By the way,MyBatis 通过JDK 动态代理记录数据库连接,预编译语句,和结果记录,分别是 ConnectionLogger、PreparedStatementLogger 和 ResultSetLogger


MyBatis 日志设计思想
http://showyoubug.cn/2024/08/23/MyBatis 日志设计思想/
作者
Dong Su
发布于
2024年8月23日
许可协议