前置知识
Executor执行器负责将sql准备好并交给Handler
Handler处理器分为几类,有专门负责sql执行的,有专门处理sql执行结果的
开启延迟加载会做什么

org.apache.ibatis.executor.statement.SimpleStatementHandler#query
看名字可以知道这个handler是专门执行sql的,通过statement执行完之后交给结果处理器来处理结果,追踪代码到核心处理方法里面后发现主要做了两件事,创建对象和赋值,赋值部分分为两种方式,自动映射和根据配置手动来映射,这些都在mapper文件中配置

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)
继续追踪,虽然分为了两个方法,但是这两个方法中都有一个核心方法如下图,红框处判断如果嵌套查询id不为0,就需要返回嵌套查询的值

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getPropertyMappingValue
接下来进入嵌套查询方法里面,具体如何查询的我们不管,重点看红框处,这里是个判断,如果有一级缓存,就调用加载方法deferLoad,如果没有的话,先判断下是否是懒加载,是的话就将数据放入一个loaderMap集合中,等待需要时使用

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getNestedQueryMappingValue
什么时候会使用延迟加载的数据
我们再回头看getRowValue这个方法,在映射前有一个createResultObject()方法,这个方法的主要作用就是创建一个代理对象,这个代理对象的作用就是在执行目标对象的get方法前需要先执行代理对象的代理方法, 这里的代理方法是EnhancedResultObjectProxyImpl的invoke方法

简单介绍下EnhancedResultObjectProxyImpl类,他是JavassistProxyFactory的一个静态内部类,他继承了MethodHandler接口,所有经过javassist代理的对象在执行get方法前都会先执行

在invoke方法中,我们可以看到真正调用延迟加载的地方,lazyLoader.load()方法主要做了两件事,如果缓存中有数据从缓存中获取,如果缓存中没有就从数据库中查

org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.EnhancedResultObjectProxyImpl#invoke
LazyLoader
延迟加载器,其中只有一个常量map集合,用来存放需要延迟加载的属性,key是属性名,value是一个静态内部类LoadPair,LoadPair中包含一些sql语句、resultObject等信息,负责具体数据的获取工作。

org.apache.ibatis.executor.loader.ResultLoaderMap
总结
回顾一下延迟加载发生的事情,首先在查询时会先生成代理对象来对目标对象的get方法进行增强,然后在对对象属性赋值时判断是否是延迟加载,如果是延迟加载就把需要执行的sql以及其他数据存放到lazyLoader中的一个map表中,等待调用。
在真正使用即调用get方法时,代理对象会先执行invoke方法,从缓存或者数据库中获取懒加载数据,然后再返回数据