Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的

Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的

    • Mybatis的启动
      • 重要配置
    • 解析XML
    • Dao与xml如何生效
      • 扫描
    • 创建SqlSession的代理
    • 创建Mapper接口的代理
      • 注入mapper
      • 执行mapper的方法
      • 获取MappedStatement对象

看到阿里这道面试题的时候,我就知道是时候看下mybatis源码了

Mybatis的启动

重要配置


    
        
        
        
        
        
        
    
    
    
    
        
        
        
        
    

解析XML

首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件

1. 根据mapper中的每句SQL生成对应的SqlSource

Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第1张图片
** 如下面demo 就生成dynamicSqlSource**
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第2张图片
生成的sqlsource
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第3张图片

2. 创建MappedStatement
XML文件中的每一个SQL标签就对应一个MappedStatement对象,这里面有两个属性很重要。
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第4张图片

3. 缓存到Configuration
所有xml解析完后,configuration对象具有所有sql信息
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第5张图片
configuration是mybatis非常重要的一个属性

Dao与xml如何生效

讲原理之前我们得知道mybatis是怎么用的

public interface UserMapper {	
	List getUserList();
}

@Service
public class UserServiceImpl implements UserService{
	@Autowired
	UserMapper userDao;
	@Override
	public List getUserList() {
		return userDao.getUserList();
	}
}

userDao没有任何实现,为什么可以执行呢?

扫描

首先配置扫描器
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第6张图片
配置了扫描器 又是怎么生效的呢

查看源码注意到 有这么一个类
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第7张图片
它实现了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();
	
	private void processBeanDefinitions(Set beanDefinitions) {
		GenericBeanDefinition definition;
		for (BeanDefinitionHolder holder : beanDefinitions) {
			definition = (GenericBeanDefinition) holder.getBeanDefinition();
			
			//将mapper接口的名称添加到构造参数
			definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
			//设置BeanDefinition的class
			definition.setBeanClass(this.mapperFactoryBean.getClass());
			//添加属性addToConfig
			definition.getPropertyValues().add("addToConfig", this.addToConfig);
			//添加属性sqlSessionFactory
			definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
			......
	}
}
 
  

复制代码处理的过程很简单,就是往BeanDefinition对象中设置了一些属性。我们重点关注两个。

设置beanClass

设置BeanDefinition对象的BeanClass为MapperFactoryBean。这意味着什么呢?以UserMapper为例,意味着当前的mapper接口在Spring容器中,beanName是userMapper,beanClass是MapperFactoryBean.class。那么在IOC初始化的时候,实例化的对象就是MapperFactoryBean对象。

设置sqlSessionFactory属性

为BeanDefinition对象添加属性sqlSessionFactory,这就意味着,在为BeanDefinition对象设置PropertyValue的时候,会调用到setSqlSessionFactory()。

创建SqlSession的代理

查看MapperFactoryBean
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第8张图片
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第9张图片
上面步骤了解到
我们之前在BeanDefinition对象添加属性sqlSessionFactory,也意味着setSqlSessionFactory()会被执行
进到里面可以看到sqlSession实际上就是SqlSessionTemplate
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第10张图片
最终是给sqlSessionProxy实例化了一个jdk代理对象
在setSqlSessionFactory这个方法里,sqlSession获取到的是SqlSessionTemplate实例。而在SqlSessionTemplate对象中,主要包含sqlSessionFactory和sqlSessionProxy,而sqlSessionProxy实际上是SqlSession接口的代理对象。

创建Mapper接口的代理

现在每一个mapper都是一个MapperFactoryBean
MapperFactoryBean是一个工厂Bean

注入mapper

现在我们通过spring注入一个Mapper
@Autowired
UserMapper userMapper
装配时会执行以下代码Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第11张图片Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第12张图片
有个问题是knownMappers是从哪儿来的呢?它为什么可以根据type接口就能获取到MapperProxyFactory实例呢?
查看DaoSupport发现它实现了InitializingBean
所以会在类初始化时 调用afterPropertiesSet,最终会调用到addMapper的方法
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第13张图片
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第14张图片

getMapper 执行到new Instance()Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第15张图片
也就是说 最终getObject获取到的是一个MapperProxy
此时注入的就是一个MapperProxy

执行mapper的方法

当执行userMapper.XXX()时,会进入Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第16张图片
重要的方法下面的mapperMethod.execute(sqlSession, args)
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第17张图片

这个方法比较简单,就是根据节点的类型,进行相应的处理。比如节点是insert 那就走到insert的逻辑

如果节点类型是select,方法返回值是list,所以代码执行了这个方法
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第18张图片
重点方法在
sqlSession.selectList(command.getName(), param, rowBounds);

上面讲到sqlSession是sqlSessionTemplate 进入方法

sqlSessionProxy也是个代理对象,总之它实际会调用到SqlSessionInterceptor.invoke()。
Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第19张图片

获取MappedStatement对象

Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的_第20张图片
重点代码几乎已经走完 下面是走到底执行底层jdbc
在这里插入图片描述

总的来说,就是
通过statement全限定类型+方法名拿到MappedStatement 对象,然后通过执行器Executor去执行具体SQL并返回

参考:
[1]: https://juejin.im/post/5c84b4515188257ea64cec43
[2]: https://juejin.im/post/5c84b4395188257e342db6eb

你可能感兴趣的:(Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的)