内容简介:看到阿里这道面试题的时候,我就知道是时候看下mybatis源码了首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。
看到阿里这道面试题的时候,我就知道是时候看下mybatis源码了
Mybatis的启动
重要配置
<!-- 会话工厂bean sqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="datasource"></property>
<!-- 别名 -->
<property name="typeAliasesPackage" value="com.jesse.bookstore.entities"></property>
<!-- sql映射文件路径 -->
<property name="mapperLocations" value="classpath*:com/zhangguo/bookstore/mapper/*Mapper.xml"></property>
</bean>
<!-- 自动扫描对象关系映射 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定会话工厂,如果当前上下文中只定义了一个则该属性可省去 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!-- 指定要自动扫描接口的基础包,实现接口 -->
<property name="basePackage" value="com.jesse.bookstore.mapper"></property>
</bean>
复制代码
解析XML
首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件
1. 根据mapper中的每句 SQL 生成对应的SqlSource
Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。
** 如下面demo 就生成dynamicSqlSource**
生成的sqlsource
2. 创建MappedStatementXML文件中的每一个SQL标签就对应一个MappedStatement对象,这里面有两个属性很重要。
3. 缓存到Configuration所有xml解析完后,configuration对象具有所有sql信息
configuration是mybatis非常重要的一个属性
Dao与xml如何生效
讲原理之前我们得知道mybatis是怎么用的
public interface UserMapper {
List<User> getUserList();
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userDao;
@Override
public List<User> getUserList() {
return userDao.getUserList();
}
}
复制代码
userDao没有任何实现,为什么可以执行呢?
扫描
首先配置扫描器
配置了扫描器 又是怎么生效的呢
查看源码注意到 有这么一个类
在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<BeanDefinitionHolder> doScan(String... basePackages) {
//调用Spring的scan方法。就是将基本包下的类注册为BeanDefinition
Set<BeanDefinitionHolder> 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<Object>();
private void processBeanDefinitions(Set<BeanDefinitionHolder> 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
上面步骤了解到 我们之前在BeanDefinition对象添加属性sqlSessionFactory,也意味着setSqlSessionFactory()会被执行 进到里面可以看到sqlSession实际上就是SqlSessionTemplate
最终是给sqlSessionProxy实例化了一个jdk代理对象 在setSqlSessionFactory这个方法里,sqlSession获取到的是SqlSessionTemplate实例。而在SqlSessionTemplate对象中,主要包含sqlSessionFactory和sqlSessionProxy,而sqlSessionProxy实际上是SqlSession接口的代理对象。
创建Mapper接口的代理
现在每一个mapper都是一个MapperFactoryBean MapperFactoryBean是一个工厂Bean
注入mapper
现在我们通过spring注入一个Mapper @Autowired UserMapper userMapper 装配时会执行以下代码
有个问题是knownMappers是从哪儿来的呢?它为什么可以根据type接口就能获取到MapperProxyFactory实例呢? 查看DaoSupport发现它实现了InitializingBean 所以会在类初始化时 调用afterPropertiesSet,最终会调用到addMapper的方法
getMapper 执行到new Instance()
执行mapper的方法
当执行userMapper.XXX()时,会进入
重要的方法下面的mapperMethod.execute(sqlSession, args)
这个方法比较简单,就是根据节点的类型,进行相应的处理。比如节点是insert 那就走到insert的逻辑
如果节点类型是select,方法返回值是list,所以代码执行了这个方法
重点方法在 sqlSession.selectList(command.getName(), param, rowBounds);
上面讲到sqlSession是sqlSessionTemplate 进入方法
,总之它实际会调用到SqlSessionInterceptor.invoke()。
重点代码几乎已经走完 下面是走到底执行底层jdbc
以上所述就是小编给大家介绍的《mybatis-spring启动到使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Music Recommendation and Discovery
Òscar Celma / Springer / 2010-9-7 / USD 49.95
With so much more music available these days, traditional ways of finding music have diminished. Today radio shows are often programmed by large corporations that create playlists drawn from a limited......一起来看看 《Music Recommendation and Discovery》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
URL 编码/解码
URL 编码/解码