一般都会使用工厂类来生成log对象,mybatis使用的自己的LogFactory,使用静态代码块来初始化logger对象
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优先选用这个框架。
有两种方案可以采用:
第一种是装饰器的模式,持有实际操作的对象,使用统一的接口来操作日志。
第二种是采用适配器模式,将某个类适配到目标类上面。继承目标接口,操作实际类的方法。
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来存储数据。
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; }