Mybatis基础支持层-日志模块

设计模式/适配器/代理/JDK动态代理/日志

Posted by Claire on June 22, 2020

mybatis-基础支持层-日志模块

mybatis给出了一套统一的日志接口供上层对接不同形式的日志框架

一、特点:适配器模式

1.设计模式的六大原则

  • 单一职责原则 : 一个类只负责唯一项职责

  • 里式替换原则 : 遵循里式替换原则,能够设计出更为合适的继承体系

  • 依赖倒置原则 : 系统的高层模块不应该依赖底层模块的实现,二者都应该依赖抽象类或接口

  • 接口隔离原则 : 一个类对另一个类的依赖应该建立在最小的接口上

  • 迪米特法则 : 一个对象应该对其他对象保持最少的了解

  • 开放-封闭原则(最基础) :程序要对扩展开放,对修改关闭

2.适配器模式

主要用于解决接口不能兼容而导致的类无法使用的问题,适配器模式会将需要适配的类转换成调用者能够使用的目标接口 大量使用适配器,使得代码逻辑过于复杂并不建议

  • 目标接口 Target :调用者能够直接使用的接口
  • 需要适配的类 Adaptee :含有真正的业务逻辑,但是器接口不能被调用者直接使用
  • 适配器 Adapter : 实现Target接口,并包装了一个Adaptee,在实现Target接口中的方法时,会将调用委托给Adaptee对象,Adaptee完成具体业务
class Adapter implement Target{
    Adaptee adaptee;

    void method(){
        adaptee.method;
    }
}
  • 好处 复用现有组件,符合开放-封闭原则

3. case in Mybatis

  • Target 接口
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);

}
  • Adapter Adaptee
//其余case代码类似
public class Log4jImpl implements Log {

  private static final String FQCN = Log4jImpl.class.getName();

  //adaptee 负责实现业务逻辑
  private final Logger log;

//在构造函数中,初始化adaptee
  public Log4jImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }

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

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

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

  @Override
  public void error(String s) {
    log.log(FQCN, Level.ERROR, s, null);
  }

  @Override
  public void debug(String s) {
    log.log(FQCN, Level.DEBUG, s, null);
  }

  @Override
  public void trace(String s) {
    log.log(FQCN, Level.TRACE, s, null);
  }

  @Override
  public void warn(String s) {
    log.log(FQCN, Level.WARN, s, null);
  }

}

二、LogFactory

LogFactory 用于初始化使用的日志框架Adapter

  • mybatis整合了多种日志框架,提供trace/debug/warn/errpr四个级别
class LogFactory{
    ....
      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 static void tryImplementation(Runnable runnable) {
      //已经初始化则不会继续
    if (logConstructor == null) {
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

//通过创建以上集中日志实现类,实现后续兼容
  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

private static void setImplementation(Class<? extends Log> implClass) {
    try {
      //获取构造方法
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      //newInstance 初始化
      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);
    }
  }
}

三、代理模式与动态代理

1. 代理模式

  • Subject 业务逻辑接口
  • RealSubject 实现接口逻辑的真正业务类
  • Proxy : 实现Subject的代理类,内含Subject realSubject对象,程序中不会直接调用RealSubject, Proxy中的接口实现中采用RealSubject的实现,但是Proxy会在前面进行前置处理,后面进行后置处理

  • 目的是:实现对RealSubject的访问控制,并在业务执行前后进行前置处理和后置处理,还可以实现延迟加载功能。mybatis中使用延迟初始化数据库连接进行查询,得到一个代理对象,没有执行任何查询的时候并没有真正获取连接,如果真正需要查询再调用代理方法进行数据库查询,很好地减少了消耗
  • 代理对象可以协调真正的RealSubject与调用者之间的关系

  • 代理模式分为:静态代理,动态代理,上面描述的是静态代理模式,当需要大量的代理的时候,会出现大量的代理类
  • 动态代理分为:基于接口的jdk动态代理,基于继承的CGLIB动态代理,这里主要面向接口方法,主要了解一下JDK动态代理

2. JDK动态代理

  • 入口: Proxy.newProxyInstance
  • 1.检验类的安全合理
  • 2.尝试从缓存获取类
  • 3.缓存中不存在,调用ProxyClassFactory apply
  • 4.检测interface集合的合法性
  • 5.根据包名、代理类前缀、编号生成代理类名称
  • 6.编译获取class,写入文件,缓存class对象
  • 7.加载代理类,返回class对象
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //检查类是否安全
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * 获取代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            //获取代理类的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //创建代理类
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }


    /**
     * a cache of proxy classes
     * 代理类缓存
     * 首先尝试从缓存中查找代理类,如果查找不到,会创建Factory 对象并调用其get方法获取代理类
     * Factory使其内部类
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

      private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //代理类缓存
        return proxyClassCache.get(loader, interfaces);
    }

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
              @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            //实现代理类加载的部分
            //1. 检查interfaces集合
            //2. 选择定义代理类的包名
            //3.代理的名称是通过包名、代理类名称前缀以及编号组成
             /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //加载代理类,返回class对象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
}

class ProxyGenerator{
     public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        //初始化class内容
        final byte[] var4 = var3.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            //写入文件
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }
}
class WeakCache{
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        //构建CacheKey对象  new CacheKey<>(key, refQueue)
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        //内部类Factory
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                //构建Factory
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
    ....

//内部类,实现Supplier ,绑定key subKey
//A factory {@link Supplier} that implements the  **lazy synchronized**
        private final class Factory implements Supplier<V> {

        private final K key;
        private final P parameter;
        private final Object subKey;
        private final ConcurrentMap<Object, Supplier<V>> valuesMap;
        ...
        //new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
          public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);//new KeyFactory()
        this.valueFactory = Objects.requireNonNull(valueFactory);// new ProxyClassFactory()
    }

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // put into reverseMap
            reverseMap.put(cacheValue, Boolean.TRUE);

            // try replacing us with CacheValue (this should always succeed)
            if (!valuesMap.replace(subKey, this, cacheValue)) {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }
        }
}

四、JDBC日志

与我们日常的使用也十分接近,我们通常希望打印sql语句,有时候show-sql=true就能够实现,就是因为底层框架做了这方面的实现,今天来看一下如何实现打印SQL、用户传入绑定参数、结果日志等

                                    BaseJdbcLogger
                                         |
                                         |
     |----------------------|-----------------------|---------------------|
ConnectionLogger     PreparedStatementLogger   ResultSetLogger      StatementLogger


上面的命名就能够清晰地看得出,每一个Logger主要面向哪一部分的功能
BaseJdbcLogger : 定义了SET_METHODS / EXECUTE_METHODS 的集合

ConnectionLogger: 封装了Connention对象,同时实现了InvocationHandler



1. BaseJdbcLogger

  • 属性
//set方法
  protected static final Set<String> SET_METHODS;
  //执行方法
  protected static final Set<String> EXECUTE_METHODS = new HashSet<>();

  //记录了PreparedStatement.set* 方法设置的键值对
  private final Map<Object, Object> columnMap = new HashMap<>();

  //记录了PreparedStatement.set* 方法的keys
  private final List<Object> columnNames = new ArrayList<>();
  //记录了PreparedStatement.set* 方法的values
  private final List<Object> columnValues = new ArrayList<>();

  //log对象
  protected final Log statementLog;
  //SQL层数,用于格式化输出SQL
  protected final int queryStack;

2.ConnectionLogger

  • 封装了Connention对象,同时实现了InvocationHandler,通过newInstance获取Connention对象,通过Invoke方法实现方法的执行
  private final Connection connection;

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

   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);
  }

@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方法,需要创建对象
      if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
        }
         //调用底层Connetion 对象的PreparedStatement方法创建 PreparedStatement对象 
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        //获取PreparedStatement的代理类
        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 {
        //其他直接调用Connection的相应方法
        return method.invoke(connection, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  
  

3.PreparedStatementLogger

 //内部PreparedStatement对象,也有newInstance获取PreparedStatement
 private final PreparedStatement statement;

 //invoke方法
 @Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      //调用了EXECTE的方法
      if (EXECUTE_METHODS.contains(method.getName())) {
        if (isDebugEnabled()) {
          debug("Parameters: " + getParameterValueString(), true);
        }
        //清空BaseJdbcLogger中的三个column缓存
        clearColumnInfo();

        if ("executeQuery".equals(method.getName())) {
          //为返回结果创建代理类
          ResultSet rs = (ResultSet) method.invoke(statement, params);
          //ResultSetLogger.newInstance
          return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
        } else {
          return method.invoke(statement, params);
        }
      } else if (SET_METHODS.contains(method.getName())) {
        //如果是调用了SET的方法,通过setColumn记录到BaseJdbcLogger的set column记录中
        if ("setNull".equals(method.getName())) {
          setColumn(params[0], null);
        } else {
          setColumn(params[0], params[1]);
        }
        return method.invoke(statement, params);
      } else if ("getResultSet".equals(method.getName())) {
        //如果是获取结果集,则创建 ResultSet代理类,ResultSetLogger.newInstance
        ResultSet rs = (ResultSet) method.invoke(statement, params);
        return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
      } else if ("getUpdateCount".equals(method.getName())) {
        int updateCount = (Integer) method.invoke(statement, params);
        if (updateCount != -1) {
          debug("   Updates: " + updateCount, false);
        }
        return updateCount;
      } else {
        return method.invoke(statement, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

4.ResultSetLogger

  • 内部也有ResultSet对象,也有newInstance方法获取ResultSet对象,
public final class ResultSetLogger extends BaseJdbcLogger implements InvocationHandler {
 //超长字段的类型
  private static final Set<Integer> BLOB_TYPES = new HashSet<>();
  //是否是ResultSet的结果集第一行
  private boolean first = true;
  //行数
  private int rows;
  //结果集
  private final ResultSet rs;
  //超大字段的列编号
  private final Set<Integer> blobColumns = new HashSet<>();

  @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);
      }
      
      Object o = method.invoke(rs, params);
      //调用结果集的next方法进行遍历
      if ("next".equals(method.getName())) {
        if ((Boolean) o) { //是否存在下一行数据
          rows++;
          if (isTraceEnabled()) {
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取数据集的列数
            final int columnCount = rsmd.getColumnCount();
            if (first) {
              //第一行
              first = false;
              //打印列名,包括超长字段
              printColumnHeaders(rsmd, columnCount);
            }
            //打印累的内容 ,打印的时候不包含超长字段
            printColumnValues(columnCount);
          }
        } else {
          debug("     Total: " + rows, false);
        }
      }
      //清空BaseJdbcLogger中的column集合
      clearColumnInfo();
      return o;
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}