内容简介:看到阿里这道面试题的时候,我就知道是时候看下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没有任何实现,为什么可以执行呢?
扫描
首先配置扫描器
配置了扫描器 又是怎么生效的呢
查看源码注意到 有这么一个类
它实现了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<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()
也就是说 最终getObject获取到的是一个MapperProxy 此时注入的就是一个MapperProxy执行mapper的方法
当执行userMapper.XXX()时,会进入
重要的方法下面的mapperMethod.execute(sqlSession, args)
这个方法比较简单,就是根据节点的类型,进行相应的处理。比如节点是insert 那就走到insert的逻辑
如果节点类型是select,方法返回值是list,所以代码执行了这个方法
重点方法在 sqlSession.selectList(command.getName(), param, rowBounds);
上面讲到sqlSession是sqlSessionTemplate 进入方法
sqlSessionProxy也是个代理对象,总之它实际会调用到SqlSessionInterceptor.invoke()。
重点代码几乎已经走完 下面是走到底执行底层jdbc
以上所述就是小编给大家介绍的《mybatis-spring启动到使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 使用 flutter 启动系统桌面
- 使用 Docker 启动 Grafana 环境
- Tomcat 7 启动分析(三)Digester 的使用
- 为什么启动后台程序需要使用nohup
- 为什么启动后台程序需要使用 nohup
- 使用 iisweb.vbs start 启动网站的方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
打造Facebook
王淮、祝文让 / 印刷工业出版社 / 2013-2-1 / 39.80元
《打造Facebook》新书发布会,王淮与读者面对面,活动链接:http://www.douban.com/event/18166913/ 这本书的书名——《打造Facebook:亲历Facebook爆发的5年》很嚣张,谁有资格可以说这句话呢,当然,扎克伯格最有资格,但他不会亲自来告诉你,至少从目前的情况来看,近几年都不大可能。而且,这不是一个人的公司。里面的每一人,尤其是工程师,既是公司文......一起来看看 《打造Facebook》 这本书的介绍吧!