所谓的延迟加载,就是将暂时不需要的对象不真正的载人内存,直到真正需要的时候再去执行加载动作。在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是如何实现的。
在上述代码中,根据setting中指定的enhancementEnabled和lazyLoadingEnabled属性来决定是否使用延迟加载以及使用延迟加载的策略,分别创建了LazyResultLoader和EnhancedLazyResultLoader对象,并且调用了loadResult方法,下面就分别看一下这两个类中的loadResult是如何实现的。
首先看一下LazyResultLoader的loadResult实现
根据上述代码可知,使用基于java反射的延迟加载实现,只会延迟加载那些类型为Collection的属性,其他属性即使配置为延迟加载也会被立即载入内存的。
然后看一下EnhancedLazyResultLoader的loadResult实现。
根据上述代码可知,采用了cglib创建代理对象的技术,而且被延迟加载的属性类型与LazyResultLoader中不同。如自定义类型的属性,在LazyResultLoader中不会被延迟加载,在EnhancedLazyResultLoader中会被延迟加载。究其根本原因在于:Java反射代理只能代理接口对象,而cglib的代理对象是基于asm的字节码增强技术,可以代理接口和对象。
在创建完了代理对象后,就是当请求真正发生时,代理对象如何处理?如何加载所需数据?下面就来看一下代理对象的invoke方法。
经过上述的讲解,结合代码,可以比较深入的理解iBATIS延迟加载的实现机制。下面对以上内容做一个简短的总结:
延迟加载的核心思想是动态代理模式。首先并不加载数据,而是创建一个代理对象,需要数据的时候由代理对象去加载所需数据。iBatis中有两种延迟加载的方法:基于java反射的LazyResultLoader和基于cglib的EnhancedLazyResultLoader。由于二者实现代理的机制不同,所以对延迟加载的支持也不相同。LazyResultLoader只能延迟加载Collection类型的属性,而EnhancedLazyResultLoader可以延迟加载Collection类型的属性,以及自定义类型的属性,而且性能方面要由于LazyResultLoader。