mybatis print JDBC log source code analysis (JDK dynamic agent)

Keywords: Java Programming MySQL Mybatis source code

mybatis supports different databases. The specific access to the database is the driver jar package provided by different database manufacturers.
For example: mysql-connector-java.jar of MySQL

The jar s provided by different manufacturers are also implemented based on the interface in the java.sql package under the JDK
For example, different database vendors have implemented their own Connection access database classes based on the Connection interface

Different manufacturers can access the database as long as they implement the interface classes provided by JDK
I have to praise the subtlety of interface oriented programming here

The function of mybatis framework is to convert database metadata into Java objects to facilitate java code operation. The specific database access operation is completed by JDBC. But many times we want to see the interaction traces between mybaits and JDBC, such as which methods mybatis calls JDBC, what parameters are passed, and what execution results are obtained.

If we print these sql logs to facilitate bug troubleshooting and analysis, we can also optimize the code according to sql. For example, by optimizing the cache use of mybatis, we can reduce the number of data accesses, reduce the pressure on the database and improve the system response time.

  1. Classes that implement JDBC sql log printing:
  2. Inheritance relationship of class:

The proxy class ConnectionLogger for connection log printing is a bit misleading. It will not be regarded as a proxy, but it will be known that it is a proxy class when you see that it implements the InvocationHandler interface.

The proxy class holds a connection interface, not a specific class. The specific implementation class depends on the connection implementation class of the database accessed by jdbc, which once again reflects the subtlety of interface oriented programming 😂, It also reflects the characteristics of java polymorphism.

  1. Let's look at the source code comments of ConnectionLogger:
/**
 * Connection proxy to add logging
 * BaseJdbcLogger The common method of several agents is implemented
 */
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {

  /**
   * Target object for dynamic proxy
   */
  private final Connection connection;

  private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
    super(statementLog, queryStack);
    this.connection = conn;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] params)
      throws Throwable {
    try {
      // Returns the class with the method method
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      /**
       * jdbc These classes under the package are equivalent to decorators
       * However, the returned classes are decorated as proxy classes
       * Determine whether the current method needs a proxy
       */
      if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
        }
        // Call the proxy method of the proxy class through reflection
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        // PreparedStatement reprocesses and encapsulates the return value and returns a new proxy class
        // Both the proxy and the proxied class need to implement the same interface, so that the polymorphic characteristics of java can be brought into play
        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);
    }
  }

  /**
   * Creates a logging version of a connection.
   * 
   * The method to create the Connection proxy. Note that this is a static method
   */
  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);
  }
}

This source code can be analyzed as a typical scenario of JDK dynamic proxy and java interface oriented programming

Posted by pulkit123 on Tue, 30 Nov 2021 00:31:47 -0800