天天看点

mybatis源码解析--mapper代理对象的生成过程

我们平常在使用mybatis的时候只需要生成mapper接口和与其对应的xml文件就行了,我们就可以把这个接口当作一个bean,可以往其他的bean中注入了。我们没有实现mapper接口,为什么可以使用接口中的方法呢?原因是虽然我们没有实现接口,但是通过配置文件,spring为我们生成了接口的代理类。

让我们从配置文件入手,从源码中一探究竟。

mybatis源码解析--mapper代理对象的生成过程

MapperScannerConfigurer这个类是负责扫描mapper接口所在的包的,它把扫描到的接口解析成一个个的bean定义(BeanDefinition),来看一下源码:

mybatis源码解析--mapper代理对象的生成过程

MapperScannerConfigurer实现了两个重要接口,如图所示。

实现BeanDefinitionRegistryPostProcessor接口,我们可以自定义注册bean过程,要实现的方法是

postProcessBeanDefinitionRegistry()

实现InitializingBean接口的afterPropertiesSet()方法,可以在bean创建之后初始化的时候做一些操作。

现在进入MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法

mybatis源码解析--mapper代理对象的生成过程

该方法内部把扫描mapper接口的工作委托给了ClassPathMapperScanner类,该类继承自ClassPathBeanDefinitionScanner,进入它的scan()方法:

mybatis源码解析--mapper代理对象的生成过程

ClassPathBeanDefinitionScanner中的doScan()方法:

mybatis源码解析--mapper代理对象的生成过程

doScan方法真正的负责生成bean定义。

ClassPathMapperScanner重载的doScan()方法:

mybatis源码解析--mapper代理对象的生成过程

重载的doScan方法对生成的mapper的bean定义做了进一步处理,进入processBeanDefinitions()方法:

mybatis源码解析--mapper代理对象的生成过程

红色部分为该方法的核心,mapperInterface设置的值是mapper接口的带包名的路径名称;

definition.setBeanClass()把原来的BeanClass的类型替换成了MapperFactoryBean类型,这个是Mapper接口加载定义阶段最重要的一步。是生成代理类的关键。

查看MapperFactoryBean的定义:

mybatis源码解析--mapper代理对象的生成过程
mybatis源码解析--mapper代理对象的生成过程
mybatis源码解析--mapper代理对象的生成过程

MapperFactoryBean实现了FactoryBean接口,实现了FactoryBean接口的类型在调用getBean(beanName)既通过名称获取对象时,返回的对象不是本身类型的对象,而是通过实现接口中的getObject()方法返回的对象。

mybatis源码解析--mapper代理对象的生成过程

MapperFactoryBean实现了FactoryBean接口InitializingBean接口,在对象初始化的时候会调用它的afterPropertiesSet方法,该方法中首先调用了checkDaoConfig()方法,MapperFactoryBean重载的checkDaoConfig()如下:

mybatis源码解析--mapper代理对象的生成过程

Configuration configuration = getSqlSession().getConfiguration();这一句中的getSqlSession()获取的SqlSession

对象就是文章开头的配置文件中配置的。

Configuration 是一个重要的配置类

mybatis源码解析--mapper代理对象的生成过程

进入 configuration.addMapper(this.mapperInterface)方法中:

mybatis源码解析--mapper代理对象的生成过程

在configuration内部对Mapper的操作都委托给了mapperRegistry对象,进入它的addMapper(type)方法,这里的参数type就是一个mapper接口的类型(如:com.st.mapper.UserMapper.java):

mybatis源码解析--mapper代理对象的生成过程

knownMappers.put(type, new MapperProxyFactory(type));这一步为我们创建了mapper 的代理工厂类对象,并把它放入了knownMappers这个Map中。MapperProxyFactory这个类我们下面再讲。

MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

parser.parse();

这两句完成了mapper接口对应的xml文件的解析,xml文件中的每一个方法都被解析成了一个MappedStatement对象(代码太长,不再展开),并添加到了configuration对象中:

mybatis源码解析--mapper代理对象的生成过程

对上面的内容的总结:

mapper接口的定义在bean加载阶段会被替换成MapperFactoryBean类型,在spring容器初始化的时候会给我们生成MapperFactoryBean类型的对象,在该对象生成的过程中调用afterPropertiesSet()方法,为我们生成了一个

MapperProxyFactory类型的对象存放于Configuration里的MapperRegistry对象中,同时解析了mapper接口对应的xml文件,把每一个方法解析成一个MappedStatement对象,存放于Configuration里的mappedStatements

这个Map集合中。

下面看一下MapperFactoryBean的getObject()方法,看看mapper代理对象是如何生成的:

mybatis源码解析--mapper代理对象的生成过程
mybatis源码解析--mapper代理对象的生成过程

跟踪代码最后调用的是MapperRegistry.getMapper()方法给我们返回了mapper代理对象。

现在来看一下MapperProxyFactory这个类:

mybatis源码解析--mapper代理对象的生成过程

MapperProxy是被代理的对象,看下这个类:

mybatis源码解析--mapper代理对象的生成过程

在invoke()方法中最终执行的是mapperMethod.execute(sqlSession, args);方法。

来看一下MapperMethod这个类:

mybatis源码解析--mapper代理对象的生成过程

execute()这个方法最终负责执行我们mapper接口中方法,它会判断要执行的方法的类型,然后调用sqlSession对应的方法类型来执行,并放回结果。

继续阅读