Introduction
mybatis implementation class did not provide the log, log component needs access to third
LogFactory static block of code to third class can be seen as load priority log slf4j> apache commons log> log4j2> log4j> jul
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();
}
});
// 省略了其他模块的加载逻辑
}
As can be seen from LogFactory class name used in the factory pattern, factory pattern do not understand you can see the following articles
Factory mode (static factory pattern, factory method pattern, abstract factory pattern) Comments
But the third party have their own log-log, mybatis adapter provides a unified model trace, debug, warn, error of four levels,
org.apache.ibatis.logging.Log interface has multiple implementation classes, the implementation class that is provided by the adapter Mybatis
, e.g. Log4jImpl, Log4j2Impl the like, an adapter class provides an implementation, following the UML class diagram
Logger figure above objects are org.apache.log4j.Logger, i.e., by the operation of the conversion will Log4jImpl Log interface adapted to the operation target Logger
Adapter pattern design follows several roles
- Target Interface (Target): The caller interfaces can be used directly, that is, the interface Log
- Adapter required classes (Adaptee): Adaptee class has a real logical, but the caller can not be used directly, that Logger object
- Adapter (Adapter): Target implements the interface, packed Adaptee objects
Of course, we generally do not configure the Logger object after Mybatis fit in the project, because the level is too low, INFO level are not
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
public static final Log log = LogFactory.getLog(Test.class);
Configured to direct the slf4j
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public static final Logger logger = LoggerFactory.getLogger(Test.class);
To adaptation of all, because the JDBC module will print the log, but it is not binding and specific logging framework, using the Log object after Mybatis adaptation
JDBC debugging, print different types of logs
In mybatis source it has a logging module jdbc package, when the log level to DEBUG level its output a lot of useful information in a dynamic manner agents, such as the output of SQL statements, the user parameter bindings that are passed, the number of rows and the like affect the SQL statement information
You might be thinking, why should print debug level log through dynamic proxy it? With log.debug () not on the list, mainly to avoid the log normal logic and coupled to a logic
BaseJdbcLogger is an abstract class that is the parent of other Logger class under jdbc package, inheritance following figure
ConnectionLogger: responsible for printing the connection information and SQL statements, and create PreparedStatementLogger
PreparedStatementLogger: responsible for printing parameter information, and create ResultSetLogger
StatementLogger: responsible for printing parameters information, and create ResultSetLogger
ResultSetLogger: responsible for printing the data results information
4 class implements the idea is the same , just to analyze ConnectionLogger
When the log level to debug, returns a Connection object after being proxy, otherwise normal Connection object
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
ConnectionLogger InvocationHandler object implements the interface, the agent returned after the Connection object
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);
}
Specific interception logic is as follows
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
// 如果是从Object继承的方法直接忽略
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果是调用prepareStatement,prepareCall,createStatement的方法,打印要执行的SQL语句
// 并返回PreparedStatement的代理对象,让PreparedStatement也具备日志能力,打印参数
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;
} 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;
} 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);
}
}
We can see the three methods were enhanced prepareStatement, prepareCall, createStatement
If the method is to call prepareStatement, prepareCall of, SQL statements to be executed print
three methods will return a proxy object PreparedStatement, so PreparedStatement also have the ability to log, print parameters
See the final printed DEBUG log
DEBUG 2020-02-24 18:09:13,647 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, `name`, phone from author
DEBUG 2020-02-24 18:09:13,766 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters:
DEBUG 2020-02-24 18:09:13,813 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
Is not looked very familiar with, it is to use dynamic proxies to achieve