MyBatis Spring 集成源码解析

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

1、Mybatis使用简介

1) SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2)SqlSession
从 SqlSessionFactory 中获取 SqlSession,既然有了 SqlSessionFactory ,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

SqlSession session = sqlSessionFactory.openSession();
try {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
  session.close();
}

诚然这种方式能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉,不过现在有了一种更直白的方式。使用对于给定语句能够合理描述参数和返回值的接口(比如说BlogMapper.class),你现在不但可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。

例如:

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
} finally {
  session.close();
}

以上方法是单独使用Mybatis的方法。下面我们就来探究一下如何与Spring集成起来使用。

2、集成Spring配置


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.spring.framework.carl" />

    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
        <property name="user" value="root"/>
        <property name="password" value="ilovemysql^^^"/>

        <property name="maxPoolSize" value="150"/>
        <property name="minPoolSize" value="10"/>
        <property name="initialPoolSize" value="20"/>
        <property name="maxIdleTime" value="3600"/>
        <property name="acquireIncrement" value="10"/>
        <property name="idleConnectionTestPeriod" value="1800"/>
    bean>

    
    <bean id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations">
            <list>
                <value>classpath:com.spring.framework.carl.user.mapper/*.xmlvalue>
            list>
        property>
    bean>

    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.spring.framework.carl.*.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="userSqlSessionFactory"/>
    bean>

beans>

以上就是Spring与MyBatis集成的基本文件了。当然有更多配置比如DataSource用数据库连接用properties文件,配置更多的MyBatis属性,如分页插件等等。上面的配置文件只是做讲解使用。

3、集成Spring原理解析

与Spring集成可以分为3个步骤.
1. 把Java类对应的Mapper接口类纳入Spring中的IOC容器管理。
2. 把Java类对应的XML命名空间添加到Mybatis中的Configuration类中的mapperRegistry(用于管理Mybatis的Mapper).
3. 使用Spring中的IOC容器扩展FactoryBean获取到Mapper的实例。(第一步纳入Spring只是接口)

1.纳入SpringIOC容器管理

MyBatis Spring 集成源码解析_第1张图片

上面时序图的主要步骤:

利用Spring的扩展BeanFactoryPostProcessor,扫描指定包下面的Mapper类,把类转换成Spring中IOC的Bean对象BeanDefinition,然后注册到IOC容器。并把BeanDefinition的BeanClass替换为MapperFactoryBean.class,这是一个Spring中的FactoryBean.这就和后面第三步的获取Mapper实例结合起来了。具体的代码如下:

  private void processBeanDefinitions(Set beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

重点代码如下:

definition.setBeanClass(this.mapperFactoryBean.getClass());

2.初始化Mybatis中的Configuration

MyBatis Spring 集成源码解析_第2张图片

上面时序图的主要步骤:

利用Spring的扩展InitializingBean,在IOC容器初始化之前,BeanDefinition设置Properties之后初始化Mybatis的Configuration。把XML的命名空间,也就是步骤一对应的Mapper类注册到Configuration中。

3.获取Mapper实例

MyBatis Spring 集成源码解析_第3张图片

上面时序图的主要步骤:

利用Spring的扩展FactoryBean可以见之前的Blog – Spring bean 之 FactoryBean,来进行实例化Mapper。时间是我们的Service中依赖注入Mapper的时候。

4、调用MyBatis中的Mapper

熟悉Spring MVC的朋友都知道其中有一个DispatcherServlet用来分发HTTP的请求。那么MyBatis中也有一个分发(一家之言,嘻嘻),MapperProxy,这个类是实现了InvocationHandler。加上与Spring集成的时候生成实例是Proxy。没有错,就是Java的动态代理,可以参看我之前的Blog – Java JDK 动态代理,本文就到这,希望能成大家起到抛砖引玉的效果。

参考文章:Mybatis官网

你可能感兴趣的:(MyBatis,Spring,Framework)