MyBatis 执行流程及源码解析
发布网友
发布时间:2024-09-29 06:47
我来回答
共1个回答
热心网友
时间:2024-10-07 00:26
在日常工作中,我们广泛使用MyBatis作为数据持久层框架,然而,你是否真正了解MyBatis的执行流程?本文将从源码角度,带你深入解析MyBatis的工作原理。
首先,我们来看一个简单的例子:在Service中调用Mapper接口。在调用时,你是否意识到注入的是什么?通过调试,可以发现实际上注入的`studentMapper`是一个类型为`MapperProxy`的代理对象。接下来,我们将从MyBatis环境初始化开始,详细分析代理对象的生成过程。
一、配置`SqlSessionFactoryBean`时,都做了什么?在进行Spring与MyBatis整合时,我们通常使用XML或注解方式配置`SqlSessionFactoryBean`。以注解方式为例,让我们一起看看`SqlSessionFactoryBean`的实现关系。
在这个过程中,我们会发现Spring中两个非常重要的接口:`FactoryBean`和`InitializingBean`。`FactoryBean`是一个Spring中特殊的Bean,通过其`getObject()`方法返回对象实例。而`SqlSessionFactoryBean`的`getObject()`方法具体实现如下:它用于创建并返回一个`SqlSessionFactory`。在Spring与MyBatis集成环境下,我们使用`SqlSessionFactoryBean`充当`SqlSessionFactory`。
`InitializingBean`接口中只包含一个方法`afterPropertiesSet()`,所有实现了该接口的类在bean初始化之前都需调用此方法。在`getObject`方法中,若`SqlSessionFactory`为空,则会调用此方法创建`SqlSessionFactory`。
通过调用`SqlSessionFactoryBuilder`的`build`方法,最终返回一个`DefaultSqlSessionFactory`实例。这个实例中保存了一个非常重要的`Configuration`对象。
二、`@MapperScan`都做了什么?在注解配置MyBatis时,通过`@MapperScan`指定Mapper存放的包,就能自动实现接口的实现类。那么,它是如何实现的呢?
深入`@MapperScan`的源码,发现其内部还有一行非常重要的注解:`ImportBeanDefinitionRegistrar`接口,提供了`registerBeanDefinitions`方法,向用户暴露了`BeanDefinitionRegistry`,即允许用户手动创建`BeanDefinition`并使用该注册器将其注册到Spring容器中。
查看`MapperScannerRegistrar`方法`registerBeanDefinitions`中的核心代码,主要创建了一个Mapper扫描器,开启扫描。
`ClassPathMapperScanner`中的`doScan`方法进一步处理生成的mapper的bean定义。
进入`processBeanDefinitions`方法,注意其中的画框代码及上方的注释,先查看从`BeanDefinitionHolder`获取`BeanDefinition`时`beanClass`初始值,然后等待`setBeanClass`执行完毕。通过`definition.setBeanClass()`将原始`BeanClass`类型替换为`MapperFactoryBean`类型。至此,完成了Mapper接口加载定义阶段中关键的一步,这也是生成代理对象`MapperProxy`的关键所在。
三、MyBatis如何生成代理对象?让我们一起看看`MapperFactoryBean`的继承关系。`MapperFactoryBean`继承自`SqlSessionDaoSupport`类,实现了`InitializingBean`接口。因此,我们首先关注`afterPropertiesSet()`方法。
`DaoSupport`中最终调用`MapperFactoryBean`中的方法。首先通过获取`sqlSession`获得非常重要的配置类`Configuration`,接下来查看`addMapper`方法,最终调用的是`MapperRegistry`的`addMapper`方法。
在红框中的代码中,我们创建了`Mapper`的代理工厂对象(还不是`Mapper`的代理对象),并将其放入`knownMappers`这个Map中。至此,只简单初始化了`MapperProxyFactory`,将我们自己的`mapper`的类型传给了它,但还没有真正生成代理对象。
`MapperRegistry`在之后的`parse`方法中完成了XML文件的解析,将每一个SQL方法解析为一个`MappedStatement`对象,并添加到`Configuration`对象中。最后,`MapperFactoryBean`返回的是`MapperProxyFactory`实例,通过它实例化并返回了一个代理对象。
四、`MapperProxy`代理对象如何执行SQL语句?在`StudentServiceImpl`中的`query`方法中插入断点,你会发现实际执行的就是代理类`MapperProxy`中的`invoke()`方法。
作为代理类的`MapperProxy`同时实现了`InvocationHandler`接口,因此`invoke`方法是执行代理逻辑的关键。在这里,最终调用`MapperMethod`的`execute`方法实际执行SQL语句。
在该方法中,根据SQL语句执行类型,调用`sqlSession`对应的方法执行并将结果返回给用户。至此,MyBatis在Spring环境下完成了一次调用。