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
的查询