Mybatis的源码学习(一):

 前言:

 **结合spring本次学习会先从spring-mybatis开始分析**

  在学习mybatis之前,应该要对spring的bean有所了解,本文略过

  先贴一下mybatis的配置:

      `<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="mapperLocations">
			<array>
				<value>classpath:mapper/**/*.xml</value>
			</array>
		</property>
		<property name="typeAliasesPackage" value="xxxx.model"/>
		<property name="plugins">
			<array>
				<bean class="com.github.pagehelper.PageHelper">
					<property name="properties">
						<value>
							dialect=mysql
							reasonable=true
						</value>
					</property>
				</bean>
			</array>
		</property>
	</bean>`
	
	`<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
           <property name="basePackage" value="xxxx.mapper"/>
     </bean>

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
`

首先,先看SqlSessionFactoryBean的配置

	`<property name="dataSource" ref="dataSource"/>`
	
	**这段即spring IOC 依赖注入的特性,将数据库的配置信息设置到该set方法中**
	 
	`<property name="mapperLocations">
        <array>
            <value>classpath:mapper/**/*.xml</value>
        </array>
    </property>`
	
	**该属性是扫描mapper的xml配置**
	
	`<property name="typeAliasesPackage" value="xxxx.model"/>`
	
	**这个配置即为model(或者entity/domain)类设置别名**
	
	`<property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageHelper">
                <property name="properties">
                    <value>
                        dialect=mysql
                        reasonable=true
                    </value>
                </property>
            </bean>
        </array>
</property>`

**这个配置是集成了分页的pahelper插件**

配置看完了,现在就开始看java代码了

配置好项目之后,找到SqlSessionFactoryBean然后启动tomcat,为什么要找这个类呢?因为这个类就是spring注入mabatis属性 的一个入口(将mybatis和spring整合)

结合上面配置内容 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 接下来就开始从源码解析:

file file

底下的所有操作都是为Configuration设置塞值,为了不影响篇幅冗余,暂且略过这些

重点说一下下面这句 file 扫描注册Mapper接口 file

  	  在初始化的时候已经将mapper解析xml的时候已经将mapper注册为一个mapper bean 了完成上述操作之后,mapper下的所有接口都已经与xml的namespace匹配上,并且mapper注册为一个代理类这就为后面的数据库操作做了一个小小的铺垫

最后一句代码就完成转化。

![file](https://choviwu.top/upload/2018/11/image-154485404911520181215140629704.png)
  调试到 SqlSessionFactoryBuilder,发现默认创建了 一个单例的DefaultSqlSessionFactory

file **即SqlSessionFactory创建完毕 **

下面是扫描tk.mapper的配置 <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer"> 看一下这个类的继承关系 file

我们看到了这个类实现了BeanDefinitionRegistryPostProcessor接口,此接口即动态的将Bean的内容进行动态修改

方法实现即在下方

file

但是,我们的tk.mapper是继承自mybatis的这个类,并重写了这个方法然后执行 所以,这块的实现是对tk mapper的功能进行加固 接下来看这里: 由于一开始spring在扫描sqlSessionFactoryBuilder的时候就已经把mapper命名空间什么的已经扫描过,所以这块不会再去注册mapperBean file 下面的注册mapper接口在前面已经执行过了 file file file 这里的配置就结束啦

接下来注入sqlSession模板类,这个类是通过动态代理的方式获取执行器并执行sql

spring开始扫描这个配置,打开调试器,看一下SqlSessionTemplate的继承图

file

它继承了SqlSession接口,还有一个SqlSession对象?这个是什么操作? 看构造方法,原来作者这样的设计是让 sqlSessionProxy代理 对SqlSession进行操作,使用代理的操作,完全对业务的耦合度降低,高,实在是高

`public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //代理对象创建执行器,并连接DB进行操作
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
  /**
   * Proxy needed to route MyBatis method calls to the proper SqlSession got
   * from Spring's Transaction Manager
   * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
   * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
   */
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     //这里的实际对象是DefaultSqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
     //执行目标方法(即调用Executor并组装成sql进行db操作)
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
  `

下一文 说一下mybatis的动态代理