知其然,知其所以然。才是考量对一切事物是否真正理解了的一个重要指标。
众所周知,Mybatis具有以下特性。
①XML与注解两种形式手写SQL(半自动化可以).
②插件机制(可拓展形式加分页,SQL性能监控等)。
③日志,缓存(一级缓存,二级缓存)。
日志简介
日志可以用来记录,跟踪,排查等作用,在JAVA中,提供了Log4j,LogBack,JDKLog(典型的就是System.out),SLf4j(适配所有日志形式)这些日志形式。
使用到了哪些设计模式?
代理,工厂。
来看看ProxyLogger类图。
代理Logger.png
①:ConnectionLogger代理java.sql.Connection
②:PreparedStatementLogger代理java.sql.PreparedStatement
③:ResultSetLogger代理java.sql.ResultSet
④:StatementLogger代理java.sql.Statement
这四个类都继承了BaseJdbcLogger并实现了java.lang.reflect.InvocationHandler接口。
日志调用代码分析
ConnectionLogger提供一个newInstance来创建代理
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
//代理
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
//执行java.sql.Connection#prepareStatement时使用PreparedStatementLogger代理
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
//执行java.sql.Connection#prepareCall{存储过程}时使用PreparedStatementLogger代理
else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
//执行java.sql.Connection#createStatement时使用StatementLogger代理
else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
BaseExecutor#getConnection中提供一个获得Connection的方法
Connection connection = transaction.getConnection();
//Debug模式情况下才开启Connection代理模式
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
//不是Debugger模式直接返回原生connection
return connection;
}
MappedStatement#Builder中用Logfactory工厂根据MappedStatementId创建了一个Logger。
line76-line80
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
mappedStatement.statementLog = LogFactory.getLog(logId);
如何开启MB日志机制
配置形式:
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
</configuration>
代码形式:
Configuration#setLogImpl(Class<? extends Log> logImpl)
//配置自己的log{
if (logImpl != null) {
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}
Configuration中预设了一些内部日志形式,以别名形式使用。
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
厚颜。
日志可以起到监控,分析排查问题等作用,不好的地方是消耗资源,拖慢速度,生产环境是禁用Debug模式的,而Mybatis设计上打印SQL日志只允许在Debug模式下被打印,设计上是很妙的。
网友评论