上半年在進(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ǔ)包 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); } 如上,mybatis提供了日志的四種級(jí)別,error->debug->trace->warn 2. 從源碼可以看到,mybatis提供了如jdk14,log4j,log4j2等日志實(shí)現(xiàn),分析常用的如log4j2源碼 // 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); } } 綜上,可以看到,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模式。 public final class LogFactory { /** // 被選定的第三方日志組件適配器的構(gòu)造方法 // 自動(dòng)掃描日志實(shí)現(xiàn),并且第三方日志插件加載優(yōu)先級(jí)如下 ...... LogFactory有默認(rèn)的日志加載順序,寫在靜態(tài)代碼塊中。默認(rèn)實(shí)現(xiàn)順序?yàn)閟lf4j->commonsLoging->log4j2->log4j->jdklog。接著分析tryImplementation方法 // 此方法調(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 } } } 查看具體的run方法,此處繼續(xù)分析log4j2的run方法。 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); } } 以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ù)方式。 ........ 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"); ......... 加載數(shù)據(jù)庫(kù)忽略,此處有Connection,Statement,ResultSet等參數(shù),查看org.apache.ibatis.logging.jdbc下的類,都有對(duì)應(yīng)的log類,先分析BaseJdbcLogger類。 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); } 再來(lái)分析ConnectionLogger類 // 實(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; } } 后續(xù)如PreparedStatementLogger,ResultSetLogger等也是一樣,都是封裝了PreparedStatement或ResultSet,在執(zhí)行真實(shí)語(yǔ)句前后進(jìn)行日志打印,打印執(zhí)行的Sql語(yǔ)句,此處用到了代理模式。熟悉AOP的可能對(duì)此有了解。 |
|
來(lái)自: 新用戶69602243 > 《待分類》