2、MapperProxy
:就是上面创建代理时的增强
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } -------------------------- final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
针对非默认,非Object方法(也就是我们的业务方法),会封装成一个MapperMethod
, 调
用的是MapperMethod.execute
3、MapperMethod
一个业务方法在执行时,会被封装成MapperMethod
, MapperMethod 执行时,又会去调用了Sqlsession
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case SELECT: ... result = sqlSession.selectOne(command.getName(), param); ... break; .... }
绕了一周,终究回到了最基本的调用方式上。
result = sqlSession.selectOne(command.getName(), param); User user = sqlSession.selectOne("com.wqd.dao.UserMapper.getById", 1);
总结下:
- 最基本用法=sqlsession.selectOne(statement.id,参数)
- Mapper=User代理类getById
---》
MapperProxy.invoke方法---》
MapperMethod.execute()---》
sqlsession.selectOne(statement.id,参数)
显然这一绕,方便了开发人员,但是对于系统来说带来的是多余开销。
五、缓存
Mybatis 还加入了缓存的设计。
分为一级缓存和二级缓存
1.一级缓存
先看长什么样子?原来就是HashMap的封装
public class PerpetualCache implements Cache { private String id; private Map<Object, Object> cache = new HashMap<Object, Object>(); public PerpetualCache(String id) { this.id = id; } }
在什么位置?作为BaseExecutor
的一个属性存在。
public abstract class BaseExecutor implements Executor { protected BaseExecutor(Configuration configuration, Transaction transaction) { this.localCache = new PerpetualCache("LocalCache"); } }
Executor
上面说过,Sqlsession的能力其实是委托Executor
完成的.Executor作为Sqlsession的一个属性存在。
所以:MyBatis一级缓存的生命周期和SqlSession一致。
2.二级缓存
2.1基本信息
二级缓存在设计上相对与一级缓存就比较复杂了。
以xml配置为例,二级缓存需要配置开启,并配置到需要用到的namespace
中。
<setting name="cacheEnabled" value="true"/>
<mapper namespace="mapper.StudentMapper"> <cache/> </mapper>
同一个namespace
下的所有MappedStatement
共用同一个二级缓存。二级缓存的生命周期跟随整个应用的生命周期,同时二级缓存也实现了同namespace
下SqlSession
数据的共享。
二级缓存配置开启后,其数据结构默认也是PerpetualCache
。这个和一级缓存的一样。
但是在构建二级缓存时,mybatis使用了一个典型的设计模式装饰模式
,对PerpetualCache
进行了一层层的增强,使得二级缓存成为一个被层层装饰过的PerpetualCache
,每装饰一层,就有不同的能力,这样一来,二级缓存就比一级缓存丰富多了。
装饰类有:
- LoggingCache:日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志
- LruCache:采用了Lru算法的Cache实现,移除最近最少使用的Key/Value
- ScheduledCache: 使其具有定时清除能力
- BlockingCache: 使其具有阻塞能力
层层装饰 private Cache setStandardDecorators(Cache cache) { try { MetaObject metaCache = SystemMetaObject.forObject(cache); if (size != null && metaCache.hasSetter("size")) { metaCache.setValue("size", size); } if (clearInterval != null) { cache = new ScheduledCache(cache); ((ScheduledCache) cache).setClearInterval(clearInterval); } if (readWrite) { cache = new SerializedCache(cache); } cache = new LoggingCache(cache); cache = new SynchronizedCache(cache); if (blocking) { cache = new BlockingCache(cache); } return cache; } catch (Exception e) { throw new CacheException("Error building standard cache decorators. Cause: " + e, e); } }
2.2如何工作
二级缓存的工作原理,还是用到装饰模式
,不过这次装饰的Executor
。使用CachingExecutor
去装饰执行SQL的Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor);//装饰 } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
当执行查询时,先从二级缓存中查询,二级缓存没有时才去走Executor
的查询