这些天在阅读MyBatis的源码,发现MyBatis源码中运用了很多设计模式,例如:模板模式,建造者模式,装饰器模式。其中最常用的就是建造者模式。下面我们就来学习下建造者模式。
建造者模式中的主要角色如下:
在MyBatis中应用到建造者模式的地方有很多,我这里举一个用的最多的点。在解析映射文件中的cache时,创建缓存构造器运用到了建造者模式。代码如下:
public class CacheBuilder {
private String id;
private Class<? extends Cache> implementation;
private List<Class<? extends Cache>> decorators;
private Integer size;
private Long clearInterval;
private boolean readWrite;
private Properties properties;
private boolean blocking;
public CacheBuilder(String id) {
this.id = id;
this.decorators = new ArrayList<Class<? extends Cache>>();
}
public CacheBuilder implementation(Class<? extends Cache> implementation) {
this.implementation = implementation;
return this;
}
public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
if (decorator != null) {
this.decorators.add(decorator);
}
return this;
}
public CacheBuilder size(Integer size) {
this.size = size;
return this;
}
public CacheBuilder clearInterval(Long clearInterval) {
this.clearInterval = clearInterval;
return this;
}
public CacheBuilder readWrite(boolean readWrite) {
this.readWrite = readWrite;
return this;
}
public CacheBuilder blocking(boolean blocking) {
this.blocking = blocking;
return this;
}
public CacheBuilder properties(Properties properties) {
this.properties = properties;
return this;
}
/**
* @return
*/
public Cache build() {
// 1. 设置默认的缓存类型(PerpetualCache)和缓存装饰器(LruCache)
setDefaultImplementations();
//通过反射创建缓存
Cache cache = newBaseCacheInstance(implementation, id);
//设额外属性,初始化Cache对象
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
// 2. 仅对内置缓存PerpetualCache应用装饰器
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
//装饰者模式一个个包装cache
cache = newCacheDecoratorInstance(decorator, cache);
//又要来一遍设额外属性
setCacheProperties(cache);
}
//3. 应用标准的装饰者,比如LoggingCache,SynchronizedCache
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
//4.如果是custom缓存,且不是日志,要加日志
cache = new LoggingCache(cache);
}
return cache;
}
private void setDefaultImplementations() {
//又是一重保险,如果为null则设默认值,
// 虽然和XMLMapperBuilder.cacheElement
// 以及MapperBuilderAssistant.useNewCache逻辑重复了,但是还是有必要
//如果用户忘记设置implementation或者人为的将implementation设为空,会导致
//build方法在构建实例时触发空指针异常。
if (implementation == null) {
// 设置默认的缓存实现类
implementation = PerpetualCache.class;
if (decorators.isEmpty()) {
// 添加LruCache装饰器
decorators.add(LruCache.class);
}
}
}
//最后附加上标准的装饰者
private Cache setStandardDecorators(Cache cache) {
try {
// 创建"元信息"对象
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
if (clearInterval != null) {
//刷新缓存间隔,怎么刷新呢,用ScheduledCache来刷,还是装饰者模式,漂亮!
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) {
//如果readOnly=false,可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
cache = new SerializedCache(cache);
}
//日志缓存
cache = new LoggingCache(cache);
//同步缓存, 3.2.6以后这个类已经没用了,考虑到Hazelcast, EhCache已经有锁机制了,所以这个锁就画蛇添足了。
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);
}
}
//省略部分代码
}
我们再来看看调用者。
// * MapperBuilderAssistant
//调用CacheBuilder构建cache,id=currentNamespace(使用建造者模式构建缓存实例)
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
如上,是一个对建造者模式的最佳应用。其中
跟标准建造者模式不同的是,Cache 对象是由反射生成的。不是直接实例化得到的。 下面,我们画个类图更好的理解下。
本文,首先对建造者模式的基本结构和角色进行了阐述,是读者对建造者有个大致的理解。然后,简要的介绍了建造者模式的优缺点。最后,通过一个例子简单介绍了建造者模式在MyBatis中的实际运用。希望本文对读者朋友们有所帮助。