如上圖所示,mybatis默認支持7種日志記錄的方式,也可以自己實現Log接口,然后將實現類通過LogFactory注入到日志工廠中。
LogFactory是日志模塊的入口,外層通過getLog獲取Log對象,然后調用Log接口方法進行日志的記錄,代碼示例:
private static final Log log = LogFactory.getLog(BaseExecutor.class); ... @Override public void close(boolean forceRollback) { try { try { rollback(forceRollback); } finally { if (transaction != null) { transaction.close(); } } } catch (SQLException e) { // Ignore. There's nothing that can be done at this point. log.warn("Unexpected exception on closing transaction. Cause: " + e); } finally { transaction = null; deferredLoads = null; localCache = null; localOutputParameterCache = null; closed = true; } }
LogFactory的初始化過程:
static { tryImplementation(new Runnable() { @Override public void run() { useSlf4jLogging(); } }); tryImplementation(new Runnable() { @Override public void run() { useCommonsLogging(); } }); tryImplementation(new Runnable() { @Override public void run() { useLog4J2Logging(); } }); tryImplementation(new Runnable() { @Override public void run() { useLog4JLogging(); } }); tryImplementation(new Runnable() { @Override public void run() { useJdkLogging(); } }); tryImplementation(new Runnable() { @Override public void run() { useNoLogging(); } }); }
private static void tryImplementation(Runnable runnable) { if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }
private static void setImplementation(Class<? extends Log> implClass) { try { Constructor<? extends Log> candidate = implClass.getConstructor(String.class); Log log = candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } }
按照順序依次去嘗試着實例化各個實例(tryImplementation方法),slf4j -> common-logging -> log4j2 -> log4j -> jdk -> nologging,當某個Log實例化成功后,就會停止下面的初始化。
實例化是setImplementation方法做的,當所有日志都沒有做配置或者缺少包的話就會失敗,所以默認的日志是NoLoggingImpl。
我們可以在mybatis配置文件中指定具體的配置類或者自定義配置類。
我們可以在代碼中調用LogFactory的方法來手動指定配置類,但是由於mybatis在初始化配置的時候將組建的各個配置都初始化好存放到configuration對象中,包括log。所以當mybatis初始化完成之后再去調用LogFactory的方法更改log對於configuration是沒有影響的,所以,我們應該在SqlSessionFactoryBuilder開始之前對LogFactory操作,下面是使用代碼配置成控制台log:
public class TestLog { public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); LogFactory.useStdOutLogging(); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = factory.openSession(); List<Gscope> result = session.selectList("getGscopes"); session.close(); System.out.println(result); } }
logging包還有個重要的組件,負責對JDBC對象攔截和日志記錄:
其中,基類BaseJdbcLogger實現了一些工具類,並封裝了column,它的4個子類ConnectionLogger,StatementLogger,PreparedStatementLogger和ResultLogger實現了InvacationHandler接口,作為JDBC對象Connection,Statement,ResultSet的代理,其中,StatementLogger和PreparedStatementLogger在ConnectionLogger中攔截statement方法並返回代理對象,ResultLogger在StatementLogger和PreparedStatementLogger中攔截execute並返回代理對象,
而ConnectionLogger是在Executor中創建的代理:
BaseExecutor.java
protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); if (statementLog.isDebugEnabled()) { return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } }
上例中的控制台打印的log如下
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Opening JDBC Connection Tue Jan 03 23:54:04 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Created connection 1635546341. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@617c74e5] ==> Preparing: select * from gscope; ==> Parameters: <== Columns: sid, id, code, name, pinyin, first_pinyin, level, parent_id, alias, orderby, lng, lat, show_in_web <== Row: 1, 22, TJ , 天津, tianji, tj, 1, 0, 天津, 1, 0, 0, 1 ...省略... <== Total: 133 Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@617c74e5] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@617c74e5] Returned connection 1635546341 to pool.