ibatis延迟加载

所谓的延迟加载,就是将暂时不需要的对象不真正的载人内存,直到真正需要的时候再去执行加载动作。在iBatis中,实现延迟加载的思路:如果一个对象的某个属性需要被延迟加载,那么首先创建这个属性对应的代理对象并返回;当真正使用时,调用这个代理对象的相关方法,invoke方法得以执行,此时,在invoke方法里面,去执行真正的加载操作。

总结起来,对于延迟加载,首先创建一个被延迟加载对象的代理对象,当使用代理对象时,就是真正需要加载的时刻,在执行invoke方法时执行加载。

在iBatis中,延迟加载相关的类主要有:ResultLoader,LazyResultLoader,EnhancedLazyResultLoader。其中ResultLoader是执行数据加载的类,LazyResultLoader和EnhancedResultLoader是延迟加载的的实现类,LazyResultLoader使用的是java的代理模型,EnhancedResultLoader使用的是cglib。

那么,在iBatis中,延迟加载是如何实现的呢?

在iBatis中,在RowMap中,如果某个result属性中指定了select属性,那么此处就有可能会发生延迟加载。在处理ResultMap时,ResultMap类中的getNestedSelectMappingValue方法会触发ResultLoader的loadResult方法。下面,就先看一下loadResult是如何实现的。

view plaincopy to clipboardprint?

public static Object loadResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType)
throws SQLException {
Object value = null;

//如果需要延迟加载,在SqlMapConfig中的setting中指定
if (client.isLazyLoadingEnabled()) {
//如果使用基于cglib的增强性能的延迟加载,需要SqlMapConfig中的setting中指定
if (client.isEnhancementEnabled()) {
EnhancedLazyResultLoader lazy = new EnhancedLazyResultLoader(client, statementName, parameterObject, targetType);
value = lazy.loadResult();
} else {
//使用基于jdk反射的延迟加载
LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType);
value = lazy.loadResult();
}
} else {
//不使用延迟加载
value = getResult(client, statementName, parameterObject, targetType);
}

return value;
}

在上述代码中,根据setting中指定的enhancementEnabled和lazyLoadingEnabled属性来决定是否使用延迟加载以及使用延迟加载的策略,分别创建了LazyResultLoader和EnhancedLazyResultLoader对象,并且调用了loadResult方法,下面就分别看一下这两个类中的loadResult是如何实现的。

首先看一下LazyResultLoader的loadResult实现

view plaincopy to clipboardprint?

public Object loadResult() throws SQLException {
//注意,只代理Collection。也就是说非Collection类型的属性不会被延迟加载
if (Collection.class.isAssignableFrom(targetType)) {
InvocationHandler handler = new LazyResultLoader(client, statementName, parameterObject, targetType);
ClassLoader cl = targetType.getClassLoader();
if (Set.class.isAssignableFrom(targetType)) {
//创建Set类型的代理对象
return Proxy.newProxyInstance(cl, SET_INTERFACES, handler);
} else {
//创建List类型的代理对象
return Proxy.newProxyInstance(cl, LIST_INTERFACES, handler);
}
} else {
//不执行延迟加载,直接加载数据
return ResultLoader.getResult(client, statementName, parameterObject, targetType);
}
}

根据上述代码可知,使用基于java反射的延迟加载实现,只会延迟加载那些类型为Collection的属性,其他属性即使配置为延迟加载也会被立即载入内存的。

然后看一下EnhancedLazyResultLoader的loadResult实现。

view plaincopy to clipboardprint?

public Object loadResult() throws SQLException {

if (DomTypeMarker.class.isAssignableFrom(targetType)) {
//这个DomTypeMarker不太清楚是干什么的
return ResultLoader.getResult(client, statementName, parameterObject, targetType);
} else if (Collection.class.isAssignableFrom(targetType)) {
//延迟加载集合类
if (Set.class.isAssignableFrom(targetType)) {
return Enhancer.create(Object.class, SET_INTERFACES, this);
} else {
return Enhancer.create(Object.class, LIST_INTERFACES, this);
}
} else if (targetType.isArray() || ClassInfo.isKnownType(targetType)) {
//数组类型和其他的集合类型,以及一些iBATIS定义的简单类型不延迟加载
return ResultLoader.getResult(client, statementName, parameterObject, targetType);
} else {
//其他情况下延迟加载
return Enhancer.create(targetType, this);
}
}

根据上述代码可知,采用了cglib创建代理对象的技术,而且被延迟加载的属性类型与LazyResultLoader中不同。如自定义类型的属性,在LazyResultLoader中不会被延迟加载,在EnhancedLazyResultLoader中会被延迟加载。究其根本原因在于:Java反射代理只能代理接口对象,而cglib的代理对象是基于asm的字节码增强技术,可以代理接口和对象。

在创建完了代理对象后,就是当请求真正发生时,代理对象如何处理?如何加载所需数据?下面就来看一下代理对象的invoke方法。

view plaincopy to clipboardprint?

public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

if ("finalize".hashCode() == method.getName().hashCode()
&& "finalize".equals(method.getName())) {
//处理finalize方法
return null;
} else {
//执行真正的加载数据操作
loadObject();
if (resultObject != null) {
try {
//客户对象向代理对象发出请求,代理对象将请求转发给已被加载出来的对象
return method.invoke(resultObject, objects);
} catch (Throwable t) {
throw ClassInfo.unwrapThrowable(t);
}
} else {
return null;
}
}
}

private synchronized void loadObject() {
if (!loaded) {
try {
loaded = true;
resultObject = ResultLoader.getResult(client, statementName, parameterObject, targetType);
} catch (SQLException e) {
throw new RuntimeException("Error lazy loading result. Cause: " + e, e);
}
}
}



经过上述的讲解,结合代码,可以比较深入的理解iBATIS延迟加载的实现机制。下面对以上内容做一个简短的总结:

延迟加载的核心思想是动态代理模式。首先并不加载数据,而是创建一个代理对象,需要数据的时候由代理对象去加载所需数据。iBatis中有两种延迟加载的方法:基于java反射的LazyResultLoader和基于cglib的EnhancedLazyResultLoader。由于二者实现代理的机制不同,所以对延迟加载的支持也不相同。LazyResultLoader只能延迟加载Collection类型的属性,而EnhancedLazyResultLoader可以延迟加载Collection类型的属性,以及自定义类型的属性,而且性能方面要由于LazyResultLoader。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值