Spring-Mybatis框架使用以及源码解析 Mybatis 源码解析

一、搭建Spring中使用Mybatis环境

 工程结构如下图所示

1、引入依赖

<properties>
        <spring.version>4.1.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

    </dependencies>

  

2、User表结构和Mybatis 源码解析是一样的

3、创建User实体类

public class User {
    private int id;
    private String name;
    private String dept;
    private String phone;
    private String website;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", dept='" + dept + '\'' +
                ", phone='" + phone + '\'' +
                ", website='" + website + '\'' +
                '}';
    }
}

  

3、resources/configs/下创建Configure.xml

mybatis 配置文件。里面主要包含Java类对应的别名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="User2" type="com.example.mybatis.model.User" />
    </typeAliases>
    <mappers>
        <mapper resource="mapper/User.xml" />
    </mappers>
</configuration>

 这里<mapper resource="mapper/User.xml" /> 要映射类的xml配置文件

4、beans.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:config/Configure.xml"></property>
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>
</beans>

  

5、resources/mapper下创建User.xml

包含了各种SQL语句

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatis.dao.UserMapper">
    <select id="getUserById" parameterType="int" resultType="User2">
        select * from user where id = #{id}
    </select>
</mapper>

  

6、创建接口com.example.mybatis.dao.UserMapper

public interface UserMapper {
    User getUserById(int id);
}

  

7、创建测试类

public class SpringMybatisTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserMapper userMapper = (UserMapper)context.getBean("userDao");
        User user = userMapper.getUserById(1);
        System.out.println(user);


    }
}

  

8、输出结果

 通过上面的过程可知,MyBatis和Spring整合最重要的是beans.xml配置

 beans.xml将dataSource、SQLSessionFactory及UserMapper注入到Spring容器中,然后获取对应的UserMapper,进行CRUD操作

二、beans.xml 分析

1、SqlSessionFactory生成

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:config/Configure.xml"></property>
        <property name="dataSource" ref="dataSource" />
    </bean>  

类似于Mybatis代码

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

  

2、userDao获取

    <bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>  

类似于Mybatis代码

SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

  

3、Spring中SqlSessionFactory生成

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:config/Configure.xml"></property>
        <property name="dataSource" ref="dataSource" />
    </bean>

 sqlSessionFactory的实现类为SqlSessionFactoryBean,定义了属性configLocation和dataSource

1) SqlSessionFactoryBean这个类

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
   ......
}

 从容器中获取SqlSessionFactoryBean,实际上调用的是getObject方法

public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
	    //sqlSessionFactory默认为空,直接走afterPropertiesSet这个方法
	    //实际上不是这样的,由于SqlSessionFactroyBean实现了InitializingBean接口,则在该Bean生成
	    //之后,直接调用afterPropertiesSet()方法来创建sqlSessionFactory,所有sqlSessionFactory是
            //已经被创建好的
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

//afterPropertiesSet()
    public void afterPropertiesSet() throws Exception {
        ...
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

//this.buildSqlSessionFactory
  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;
    ...
    ...
    else if (this.configLocation != null) {
      //1、解析Configure.xml配置文件
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      //2、获取配置中心Configuration
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      }
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }

    //3、以下的操作是对Configuration中各种属性进行set操作
    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    if (this.vfs != null) {
      configuration.setVfsImpl(this.vfs);
    }

    if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
      }
    }

    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }
    ....
    ....

    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

    //4、最终还是通过sqlSessionFactoryBuilder生成sqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

最终还是通过sqlSessionFactoryBuilder生成sqlSessionFactory,和原生Mybatis代码是一致的。

 

4、Spring中的Mapper接口(也就是这里的userDao实例,对应UserMapper接口)

由beans.xml中的配置可知

    <bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>  

实现类为MapperFactoryBean,定义了mapperInterface和sqlSessionFactory属性。

mapperInterface要指定UserMapper接口的全路径

1) MapperFactoryBean

实现了 FactoryBean接口,真正返回的是getObject()返回的对象

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
	  @Override
  	public T getObject() throws Exception {
		//getSqlSession()返回的是SqlSessionDaoSupport的sqlSession属性。
		//MapperFactoryBean在Spring中真正返回的是getSqlSession().getMapper方法返回的Mapper接口
    		return getSqlSession().getMapper(this.mapperInterface);
 	 }

}

  

 2) MapperFactoryBean.afterPropertiesSet()

具体实现在DaoSupport类中

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
        //MapperFactoryBean.checkDaoConfig中实现
	this.checkDaoConfig();
	//Spring没有关于initDao的实现,可以自己实现
        try {
            this.initDao();
        } catch (Exception var2) {
            throw new BeanInitializationException("Initialization of DAO failed", var2);
        }
    }


//MapperFactoryBean.checkDaoConfig
 protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        //主要是将接口添加进configuration
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }  

总结:

MapperFactoryBean初始化的时候就通过afterPropertiesSet()方法把Mapper接口添加进Configuration;

 MapperFactoryBean.getObject()返回的也就是从Configuration中获取的Mapper接口。

 MapperFactoryBean.getObject()方法类似于原生Mybatis方法

SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

  

通过对Spring-Mybatis的源码分析,本质上还是Mybatis,只不过容器帮我们生产了SqlSessionFactory和SqlSession,同时把Mapper加载进Configuration,可以直接通过getBean的方式来获取Mapper

参考:Spring-Mybatis框架使用与源码解析

猜你喜欢

转载自www.cnblogs.com/linlf03/p/12584975.html
今日推荐