首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件
1. 根据mapper中的每句SQL生成对应的SqlSource
Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。
** 如下面demo 就生成dynamicSqlSource**
生成的sqlsource
2. 创建MappedStatement
XML文件中的每一个SQL标签就对应一个MappedStatement对象,这里面有两个属性很重要。
3. 缓存到Configuration
所有xml解析完后,configuration对象具有所有sql信息
configuration是mybatis非常重要的一个属性
讲原理之前我们得知道mybatis是怎么用的
public interface UserMapper {
List getUserList();
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userDao;
@Override
public List getUserList() {
return userDao.getUserList();
}
}
userDao没有任何实现,为什么可以执行呢?
查看源码注意到 有这么一个类
它实现了BeanDefinitionRegistryPostProcessor。
在spring中,它可以 动态的注册Bean信息,方法 postProcessBeanDefinitionRegistry()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//创建ClassPath扫描器,设置属性,然后调用扫描方法
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAnnotationClass(this.annotationClass);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
//如果配置了annotationClass,就将其添加到includeFilters
scanner.registerFilters();
scanner.scan(this.basePackage);
}
复制代码ClassPathMapperScanner继承自Spring中的类ClassPathBeanDefinitionScanner,所以scan方法会调用到父类的scan方法,而在父类的scan方法中又调用到子类的doScan方法。
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public Set doScan(String... basePackages) {
//调用Spring的scan方法。就是将基本包下的类注册为BeanDefinition
Set beanDefinitions = super.doScan(basePackages);
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
}
super.doScan(basePackages)是Spring中的方法。我主要看它返回的是BeanDefinition的集合。
3、配置BeanDefinition
上面已经扫描到了所有的Mapper接口,并将其注册为BeanDefinition对象。接下来调用processBeanDefinitions()要配置这些BeanDefinition对象。
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private MapperFactoryBean> mapperFactoryBean = new MapperFactoryBean
复制代码处理的过程很简单,就是往BeanDefinition对象中设置了一些属性。我们重点关注两个。
设置beanClass
设置BeanDefinition对象的BeanClass为MapperFactoryBean>。这意味着什么呢?以UserMapper为例,意味着当前的mapper接口在Spring容器中,beanName是userMapper,beanClass是MapperFactoryBean.class。那么在IOC初始化的时候,实例化的对象就是MapperFactoryBean对象。
设置sqlSessionFactory属性
为BeanDefinition对象添加属性sqlSessionFactory,这就意味着,在为BeanDefinition对象设置PropertyValue的时候,会调用到setSqlSessionFactory()。
查看MapperFactoryBean
上面步骤了解到
我们之前在BeanDefinition对象添加属性sqlSessionFactory,也意味着setSqlSessionFactory()会被执行
进到里面可以看到sqlSession实际上就是SqlSessionTemplate
最终是给sqlSessionProxy实例化了一个jdk代理对象
在setSqlSessionFactory这个方法里,sqlSession获取到的是SqlSessionTemplate实例。而在SqlSessionTemplate对象中,主要包含sqlSessionFactory和sqlSessionProxy,而sqlSessionProxy实际上是SqlSession接口的代理对象。
现在每一个mapper都是一个MapperFactoryBean
MapperFactoryBean是一个工厂Bean
现在我们通过spring注入一个Mapper
@Autowired
UserMapper userMapper
装配时会执行以下代码
有个问题是knownMappers是从哪儿来的呢?它为什么可以根据type接口就能获取到MapperProxyFactory实例呢?
查看DaoSupport发现它实现了InitializingBean
所以会在类初始化时 调用afterPropertiesSet,最终会调用到addMapper的方法
getMapper 执行到new Instance()
也就是说 最终getObject获取到的是一个MapperProxy
此时注入的就是一个MapperProxy
当执行userMapper.XXX()时,会进入
重要的方法下面的mapperMethod.execute(sqlSession, args)
这个方法比较简单,就是根据节点的类型,进行相应的处理。比如节点是insert 那就走到insert的逻辑
如果节点类型是select,方法返回值是list,所以代码执行了这个方法
重点方法在
sqlSession.selectList(command.getName(), param, rowBounds);
上面讲到sqlSession是sqlSessionTemplate 进入方法
sqlSessionProxy也是个代理对象,总之它实际会调用到SqlSessionInterceptor.invoke()。
总的来说,就是
通过statement全限定类型+方法名拿到MappedStatement 对象,然后通过执行器Executor去执行具体SQL并返回
参考:
[1]: https://juejin.im/post/5c84b4515188257ea64cec43
[2]: https://juejin.im/post/5c84b4395188257e342db6eb