通常都會使用工廠類來生成log對象,mybatis使用的本身的LogFactory,使用靜態代碼塊來初始化logger對象 java
static { tryImplementation(new Runnable() { public void run() { useSlf4jLogging(); //這個地方沒有使用線程的方式,都是按順序執行的。當某一個logger類被初始化成功後,後面的logger類就不會在初始化。 } }); 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(); } }); }mybatis對大部分主流的日誌框架都包裝了一層。已slf4j爲例:
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);實例化封裝好的slf4j類。
public Slf4jImpl(String clazz) { Logger logger = LoggerFactory.getLogger(clazz); if (logger instanceof LocationAwareLogger) { try { // check for slf4j >= 1.6 method signature logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class); log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger); //採用的slf4j的LocationAwareLogger return; } catch (SecurityException e) { // fail-back to Slf4jLoggerImpl } catch (NoSuchMethodException e) { // fail-back to Slf4jLoggerImpl } } // Logger is not LocationAwareLogger or slf4j version < 1.6 log = new Slf4jLoggerImpl(logger);//這裏作了一個兼容操做,低版本的時候採用的裝飾器的操做,實際操做的是slf4j的logger日誌操做 }取得slf4j的logger對象,應爲slf4j自己已經對主流框架作了選擇的操做,全部mybatis優先選用這個框架。
有兩種方案能夠採用: sql
第一種是裝飾器的模式,持有實際操做的對象,使用統一的接口來操做日誌。 apache
第二種是採用適配器模式,將某個類適配到目標類上面。繼承目標接口,操做實際類的方法。 緩存
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());內部是從線程裏面拿取的ErrorContext的實例,保證在一個線程內部均可以取到同一個 ErrorContext
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>(); public static ErrorContext instance() { ErrorContext context = LOCAL.get(); if (context == null) { --從ThreadLocal裏面獲取ErrorContext,若是沒有context實例,先建一個。 context = new ErrorContext(); LOCAL.set(context); } return context; } public ErrorContext store() { stored = this; LOCAL.set(new ErrorContext()); return LOCAL.get(); }
在BaseExecutor裏面,在查詢裏面在session內,都保持同一個localCache來存儲數據,內部使用的是HashMap來存儲數據。 session
PerpetualCache: private Map<Object, Object> cache = new HashMap<Object, Object>(); --存儲結果集,根據key值來獲取和存儲數據key的生成規則是CacheKey來決定的。
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
看下里面的equals和hashcode方法就知道哪些決定這個cachekey惟一性:
public boolean equals(Object object) { if (this == object) return true; if (!(object instanceof CacheKey)) return false; final CacheKey cacheKey = (CacheKey) object; if (hashcode != cacheKey.hashcode) return false; if (checksum != cacheKey.checksum) return false; if (count != cacheKey.count) return false; for (int i = 0; i < updateList.size(); i++) { Object thisObject = updateList.get(i); Object thatObject = cacheKey.updateList.get(i); if (thisObject == null) { if (thatObject != null) return false; } else { if (!thisObject.equals(thatObject)) return false; } } return true; }比較的是updateList裏面的值是否相等,在前面放置了多個值,肯定每個都是相等的時候,產生的cachekey纔是一致的。
private void doUpdate(Object object) { int baseHashCode = object == null ? 1 : object.hashCode(); count++; checksum += baseHashCode; baseHashCode *= count; --這個是用來檢驗個數的 hashcode = multiplier * hashcode + baseHashCode; --這個是用來生成新的HashCode,使用數據的hashCode來生成新的hashCode,來做爲判斷依據。 updateList.add(object); }有個注意的點是外部的對象的若是非java基本數據類型的時候,hashCode的生成都須要被重寫,保證在某些狀況下是一致的。
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) throw new ExecutorException("Executor was closed."); CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); --String對象 cacheKey.update(rowBounds.getOffset()); --int型的位移 cacheKey.update(rowBounds.getLimit()); --int型的限制 cacheKey.update(boundSql.getSql()); --String型的sql List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); for (int i = 0; i < parameterMappings.size(); i++) { // mimic DefaultParameterHandler logic ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } cacheKey.update(value); } } return cacheKey; }