一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

智能營(yíng)銷總部:編程教學(xué) myBatis源碼解析

 新用戶69602243 2020-07-28

上半年在進(jìn)行知識(shí)儲(chǔ)備,下半年?duì)幦懸稽c(diǎn)好的文章來(lái)記錄自己源碼之路。在學(xué)習(xí)源碼的路上也掌握了一些設(shè)計(jì)模式,可所謂一舉兩得。本次打算寫Mybatis的源碼解讀。

準(zhǔn)備工作

1. 下載mybatis源碼

下載地址:https://github.com/mybatis/mybatis-3 

2. 下載mybatis-parent源碼

下載地址:https://github.com/mybatis/parent

3. 編譯

進(jìn)入mybatis-paren所在文件夾

mvn clean install

進(jìn)入mybatis所在文件夾

mvn clean 
mvn install -Dmaven.test.skip=true

4. 用IDEA或Eclipse打開mybatis即可

源碼分析-日志模塊

1. 日志基礎(chǔ)包

復(fù)制代碼

package org.apache.ibatis.logging;// mybatis自定義接口,提供四種級(jí)別 error->debug->trace->warnpublic 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);
}

復(fù)制代碼

如上,mybatis提供了日志的四種級(jí)別,error->debug->trace->warn

2. 從源碼可以看到,mybatis提供了如jdk14,log4j,log4j2等日志實(shí)現(xiàn),分析常用的如log4j2源碼

復(fù)制代碼

// log4j的適配器public class Log4j2Impl implements Log {  // 真正提供日志能力的log4j的日志類
  private Log log;  // 構(gòu)造方法,導(dǎo)入真正的實(shí)現(xiàn)類
  public Log4j2Impl(String clazz) {
    Logger logger = LogManager.getLogger(clazz);    if (logger instanceof AbstractLogger) {
      log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);
    } else {
      log = new Log4j2LoggerImpl(logger);
    }
  }  
  public boolean isDebugEnabled() {    return log.isDebugEnabled();
  }  public boolean isTraceEnabled() {    return log.isTraceEnabled();
  }  // 調(diào)用真正的實(shí)現(xiàn)方法
  public void error(String s, Throwable e) {
    log.error(s, e);
  } // 調(diào)用真正的實(shí)現(xiàn)方法
  public void error(String s) {
    log.error(s);
  }// 調(diào)用真正的實(shí)現(xiàn)方法
  public void debug(String s) {
    log.debug(s);
  }// 調(diào)用真正的實(shí)現(xiàn)方法
  public void trace(String s) {
    log.trace(s);
  }// 調(diào)用真正的實(shí)現(xiàn)方法
  public void warn(String s) {
    log.warn(s);
  }

}

復(fù)制代碼

綜上,可以看到,mybatis并沒(méi)有提供真正的日志實(shí)現(xiàn)接口,只是定義了一套自己的日志接口,其實(shí)現(xiàn)交給真正的具體日志類(如log4j,log4j2)。此處用到了適配器模式。比如log4j2,mybatis提供自定義接口,提供log4j2適配器繼承自定義接口,在log4j2適配器里調(diào)用log4j2的真實(shí)方法來(lái)實(shí)現(xiàn)自己的接口。

3.各日志默認(rèn)調(diào)用流程

看源碼包中有很多日志實(shí)現(xiàn),那具體的默認(rèn)調(diào)用流程是怎樣。查看org.apache.ibatis.logging包中的LogFactory類??疵志椭朗怯玫搅斯S模式。

復(fù)制代碼


public final class LogFactory {


/**
* Marker to be used by logging implementations that support markers
*/
public static final String MARKER = "MYBATIS";


// 被選定的第三方日志組件適配器的構(gòu)造方法
private static Constructor<? extends Log> logConstructor;


// 自動(dòng)掃描日志實(shí)現(xiàn),并且第三方日志插件加載優(yōu)先級(jí)如下
// 類加載時(shí)會(huì)默認(rèn)實(shí)現(xiàn)靜態(tài)方法,實(shí)現(xiàn)順序?yàn)閟lf4j->commonsLoging->log4j2->log4j->jdklog
static {
// 調(diào)用tryImplementation方法
tryImplementation(new Runnable() {
public void run() {
useSlf4jLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useCommonsLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
public void run() {
useLog4JLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useJdkLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useNoLogging();
}
});
}

......

復(fù)制代碼

LogFactory有默認(rèn)的日志加載順序,寫在靜態(tài)代碼塊中。默認(rèn)實(shí)現(xiàn)順序?yàn)閟lf4j->commonsLoging->log4j2->log4j->jdklog。接著分析tryImplementation方法

復(fù)制代碼

// 此方法調(diào)用的是線程的run方法,仔細(xì)看,是直接run,不是start,所以不是多線程
  private static void tryImplementation(Runnable runnable) {    // 判斷全局的日志構(gòu)造方法是否為空,若為空,則調(diào)用線程的run方法
    if (logConstructor == null) {      try {
        runnable.run();
      } catch (Throwable t) {         // 對(duì)沒(méi)有具體實(shí)現(xiàn)的日志類,直接忽略,不拋異常,        // ignore      }
    }
  }

復(fù)制代碼

查看具體的run方法,此處繼續(xù)分析log4j2的run方法。

復(fù)制代碼

 public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  } // 使用到了java的反射機(jī)制
  private static void setImplementation(Class<? extends Log> implClass) {    try {      // 利用反射獲取log4j2的真實(shí)構(gòu)造方法
      Constructor<? extends Log> candidate = implClass.getConstructor(new Class[] { String.class });      // 獲取log4j2的實(shí)例
      Log log = candidate.newInstance(new Object[] { LogFactory.class.getName() });
      log.debug("Logging initialized using '" + implClass + "' adapter.");      // 賦值給全局變量logConstructor
      logConstructor = candidate;
    } catch (Throwable t) {      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

復(fù)制代碼

以log4j2為例,分析了具體的日志實(shí)現(xiàn)。但我們使用mybatis時(shí)通常會(huì)在執(zhí)行sql的時(shí)候,對(duì)sql進(jìn)行打印輸出。所以接下來(lái)查看mybatis如何使用這些日志接口。

4. 分析org.apache.ibatis.logging.jdbc下的類

在源碼分析前,回顧下jdbc連接數(shù)據(jù)庫(kù)方式。

復(fù)制代碼

         ........
         Class.forName("com.mysql.jdbc.Driver");         //2.獲得數(shù)據(jù)庫(kù)鏈接
         Connection conn=DriverManager.getConnection(URL, USER, PASSWORD);         //3.通過(guò)數(shù)據(jù)庫(kù)的連接操作數(shù)據(jù)庫(kù),實(shí)現(xiàn)增刪改查(使用Statement類)
         Statement st=conn.createStatement();
         ResultSet rs=st.executeQuery("select * from user");
         .........

復(fù)制代碼

加載數(shù)據(jù)庫(kù)忽略,此處有Connection,Statement,ResultSet等參數(shù),查看org.apache.ibatis.logging.jdbc下的類,都有對(duì)應(yīng)的log類,先分析BaseJdbcLogger類。

復(fù)制代碼

public abstract class BaseJdbcLogger {  // PrepareedStatement下的所有set方法
  protected static final Set<String> SET_METHODS = new HashSet<String>();  protected static final Set<String> EXECUTE_METHODS = new HashSet<String>();  // PrepareecdStatement 設(shè)置的key value pair
  private Map<Object, Object> columnMap = new HashMap<Object, Object>();  // PreparedStatement 設(shè)置的column
  private List<Object> columnNames = new ArrayList<Object>();  // PrepareedStatement 設(shè)置的value
  private List<Object> columnValues = new ArrayList<Object>();  protected Log statementLog;  protected int queryStack;  /*
   * Default constructor   */
  public BaseJdbcLogger(Log log, int queryStack) {    this.statementLog = log;    if (queryStack == 0) queryStack = 1;    this.queryStack = queryStack;
  }  // 初始化默認(rèn)的一些set方法
  static {
    SET_METHODS.add("setString");
    SET_METHODS.add("setInt");
    SET_METHODS.add("setByte");
    SET_METHODS.add("setShort");
    SET_METHODS.add("setLong");
    SET_METHODS.add("setDouble");
    SET_METHODS.add("setFloat");
    SET_METHODS.add("setTimestamp");
    SET_METHODS.add("setDate");
    SET_METHODS.add("setTime");
    SET_METHODS.add("setArray");
    SET_METHODS.add("setBigDecimal");
    SET_METHODS.add("setAsciiStream");
    SET_METHODS.add("setBinaryStream");
    SET_METHODS.add("setBlob");
    SET_METHODS.add("setBoolean");
    SET_METHODS.add("setBytes");
    SET_METHODS.add("setCharacterStream");
    SET_METHODS.add("setClob");
    SET_METHODS.add("setObject");
    SET_METHODS.add("setNull");

    EXECUTE_METHODS.add("execute");
    EXECUTE_METHODS.add("executeUpdate");
    EXECUTE_METHODS.add("executeQuery");
    EXECUTE_METHODS.add("addBatch");
  }  // setColumn方會(huì)記錄設(shè)置的column和對(duì)應(yīng)的value
  protected void setColumn(Object key, Object value) {
    columnMap.put(key, value);
    columnNames.add(key);
    columnValues.add(value);
  }

復(fù)制代碼

再來(lái)分析ConnectionLogger類

復(fù)制代碼

// 實(shí)現(xiàn)InvocationHandler就知道是個(gè)代理類public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {  // 真正的connection
  private Connection connection;  private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {    super(statementLog, queryStack);    this.connection = conn;
  }  // 增強(qiáng)
  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方法
      if ("prepareStatement".equals(method.getName())) {        // 打印sql參數(shù)
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }        // 調(diào)用connection真實(shí)方法
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);        // 將生成的PreparedStatement也構(gòu)建成代理對(duì)象
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);        return stmt;
      } else if ("prepareCall".equals(method.getName())) { // 若是prepareCall方法
        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())) { // 若是createStatement方法
        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
   *
   * @param conn - the original connection
   * @return - the connection with logging   */
  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);
  }  /*
   * return the wrapped connection
   *
   * @return the connection   */
  public Connection getConnection() {    return connection;
  }

}

復(fù)制代碼

后續(xù)如PreparedStatementLogger,ResultSetLogger等也是一樣,都是封裝了PreparedStatement或ResultSet,在執(zhí)行真實(shí)語(yǔ)句前后進(jìn)行日志打印,打印執(zhí)行的Sql語(yǔ)句,此處用到了代理模式。熟悉AOP的可能對(duì)此有了解。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    十八禁日本一区二区三区| 久久免费精品拍拍一区二区| 久热久热精品视频在线观看 | 欧美中文日韩一区久久| 日韩免费av一区二区三区| 黑鬼糟蹋少妇资源在线观看 | 久久精品亚洲情色欧美| 亚洲第一区欧美日韩在线| 欧美精品在线播放一区二区| 亚洲婷婷开心色四房播播| 国产免费成人激情视频| 日本少妇中文字幕不卡视频| 精品推荐久久久国产av| 国产又粗又黄又爽又硬的| 熟女白浆精品一区二区| 国产伦精品一区二区三区精品视频| 欧美同性视频免费观看| 国产成人精品午夜福利| 成人午夜激情在线免费观看| 婷婷色网视频在线播放| 欧美日韩视频中文字幕| 欧美日韩免费观看视频| 东京热男人的天堂久久综合| 激情综合五月开心久久| 男人和女人草逼免费视频| 国产精品一区二区丝袜| 99免费人成看国产片| 好吊妞视频免费在线观看| 老外那个很粗大做起来很爽| 欧美日韩亚洲国产精品| 色丁香之五月婷婷开心| 国产又长又粗又爽免费视频 | 一区二区三区日本高清| 婷婷九月在线中文字幕| 99久久国产亚洲综合精品| 亚洲中文字幕免费人妻| 风间中文字幕亚洲一区| 人人妻人人澡人人夜夜| 欧美日韩国产的另类视频| 在线欧美精品二区三区| 人妻精品一区二区三区视频免精|