spring整合mybatis原理

前言

               MyBatis相信很多人都会使用,但是当MyBatis整合到了spring中,我们发现在Spring中使用更加方便了。例如获取Dao的实例,在Spring的我们只需要使用注入的方式就可以了使用Dao了,完全不需要调用SqlSession的getMapper方法去获取Dao的实例,更不需要我们去管理SqlSessionFactory,也不需要去创建SqlSession之类的了,对于插入操作也不需要我们commit。

               既然那么方便,Spring到底为我们做了哪些工作呢,它如何将MyBatis整合到Spring中的呢,Spring在整合MyBatis时候做了哪些封装,以及做了哪些拓展,又是怎么实现这些封装以及拓展的,让我们来打开这一部分的源代码,一探究竟。

               首先我们来先回顾下MyBatis的用法,以及Spring中MyBatis的使用方法。


MyBatis使用介绍

         单单MyBatis的使用比较简单,让我们来简单回顾一下他的使用方法。

建立PO 

po用于对于数据库中数据的映射,使得开发者更加专注于Java类的使用,而不是对数据库的操作

  1. /** 
  2.  * @author: Fighter168 
  3.  */  
  4. public class Person {  
  5.     private String id;  
  6.     private String name;  
  7. //set get 方法、、、  
  8. }  
/**
 * @author: Fighter168
 */
public class Person {
	private String id;
	private String name;
//set get 方法、、、
}


建立Mapper

mapper是数据库操作的映射文件,也就是我们常说的dao文件

  1. /** 
  2.  * @author: Fighter168 
  3.  */  
  4. public interface PersonDao {  
  5.   
  6.     public List<Person> query();  
  7.       
  8.     public void save(Person p);  
  9.       
  10.     public void delete(String id);  
  11. }  
/**
 * @author: Fighter168
 */
public interface PersonDao {

	public List<Person> query();
	
	public void save(Person p);
	
	public void delete(String id);
}


建立配置文件

        配置文件主要用于程序中可变性高的设置,Mybatis的配置文件主要存在于configuration.xml中,当然configuration.xml中省略了其他mybatis的配置,例如settings里面的配置等等,如果没有玩过MyBatis的同学可以去参考网上MyBatis的教程自己去了解了解。

  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <!DOCTYPE configuration    
  3.   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"    
  4.   "http://mybatis.org/dtd/mybatis-3-config.dtd">    
  5. <configuration>    
  6.     <!-- 对事务的管理和连接池的配置 -->    
  7.     <environments default="development">    
  8.         <environment id="development">    
  9.             <transactionManager type="JDBC" />    
  10.             <dataSource type="POOLED">    
  11.                    <property name="driver" value="com.mysql.jdbc.Driver" />  
  12.                    <property name="url" value="jdbc:mysql://localhost:3306/test" />  
  13.                    <property name="username" value="root" />  
  14.                    <property name="password" value="root" />    
  15.             </dataSource>    
  16.         </environment>    
  17.     </environments>    
  18.         
  19.     <!-- mapping 文件路径配置 -->    
  20.     <mappers>    
  21.         <mapper resource="resource/PersonMapper.xml" />    
  22.     </mappers>    
  23. </configuration>  
<?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>  
    <!-- 对事务的管理和连接池的配置 -->  
    <environments default="development">  
        <environment id="development">  
            <transactionManager type="JDBC" />  
            <dataSource type="POOLED">  
                   <property name="driver" value="com.mysql.jdbc.Driver" />
                   <property name="url" value="jdbc:mysql://localhost:3306/test" />
                   <property name="username" value="root" />
                   <property name="password" value="root" />  
            </dataSource>  
        </environment>  
    </environments>  
      
    <!-- mapping 文件路径配置 -->  
    <mappers>  
        <mapper resource="resource/PersonMapper.xml" />  
    </mappers>  
</configuration>


建立映射文件

映射文件对应于Mybatis全局配置中的mappers配置属性,主要用于建立对应数据库操作接口的SQL映射。

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd" >  
  3. <mapper namespace="net.itaem.dao.PersonDao" >  
  4.     
  5.   <resultMap id="resultMap" type="net.itaem.po.Person" >  
  6.         <result column="id" property="id" jdbcType="CHAR" />  
  7.         <result column="name" property="name" jdbcType="CHAR" />  
  8.   </resultMap>  
  9. <!--添加-->   
  10.  <insert id="save"  parameterType="net.itaem.po.Person">  
  11.  insert into person(id,name) value(#{id,jdbcType=CHAR},#{name,jdbcType=CHAR})  
  12.  </insert>  
  13. <!--查询-->  
  14.  <select id="query"  resultMap="resultMap">  
  15.  select * from person  
  16.  </select>  
  17.  <!--删除-->  
  18. <delete id="delete" parameterType="java.lang.String">  
  19.  delete from person  where id=#{id,jdbcType=CHAR}  
  20. </delete>  
  21. </mapper>  
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="net.itaem.dao.PersonDao" >
  
  <resultMap id="resultMap" type="net.itaem.po.Person" >
	    <result column="id" property="id" jdbcType="CHAR" />
	    <result column="name" property="name" jdbcType="CHAR" />
  </resultMap>
<!--添加--> 
 <insert id="save"  parameterType="net.itaem.po.Person">
 insert into person(id,name) value(#{id,jdbcType=CHAR},#{name,jdbcType=CHAR})
 </insert>
<!--查询-->
 <select id="query"  resultMap="resultMap">
 select * from person
 </select>
 <!--删除-->
<delete id="delete" parameterType="java.lang.String">
 delete from person  where id=#{id,jdbcType=CHAR}
</delete>
</mapper>


建立测试类

           下面通过编写使用MyBatis实现查询的测试用例,比较简单,所以就不写太多注释了。

  1. /** 
  2.  * @author: Fighter168 
  3.  */  
  4. public class Test {  
  5.   
  6.     public static void main(String[] args) throws Exception {  
  7.         Reader reader=Resources.getResourceAsReader("resource/configuration.xml");  
  8.         SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);  
  9.         SqlSession session=sessionFactory.openSession();  
  10.         PersonDao personDao=session.getMapper(PersonDao.class);  
  11.         Person person=new Person("11","Fighter168");  
  12.         personDao.save(person);  
  13.         //这里一定要提交,不然数据无法插入  
  14.         session.commit();  
  15.         session.close();  
  16.     }  
  17. }  
/**
 * @author: Fighter168
 */
public class Test {

	public static void main(String[] args) throws Exception {
		Reader reader=Resources.getResourceAsReader("resource/configuration.xml");
		SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
		SqlSession session=sessionFactory.openSession();
		PersonDao personDao=session.getMapper(PersonDao.class);
		Person person=new Person("11","Fighter168");
		personDao.save(person);
		//这里一定要提交,不然数据无法插入
		session.commit();
		session.close();
	}
}




Spring中使用MyBatis介绍


             Spring中使用MyBatis更加简单,为了对比一下,我们来先看看下面的小例子。

创建Spring配置文件

          里面主要配置的是数据源,sqlSessionFactory和Dao的信息。
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  5.      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">   
  6.          <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
  7.          <property name="url" value="jdbc:mysql://localhost:3306/test"/>  
  8.          <property name="username" value="root"/>  
  9.          <property name="password" value="123abc"/>  
  10.          <!-- 连接池启动时候的初始连接数 -->  
  11.          <property name="initialSize" value="10"/>  
  12.          <!-- 最小空闲值 -->  
  13.          <property name="minIdle" value="5"/>  
  14.          <!-- 最大空闲值 -->  
  15.          <property name="maxIdle" value="20"/>  
  16.          <property name="maxWait" value="2000"/>  
  17.          <!-- 连接池最大值 -->  
  18.          <property name="maxActive" value="50"/>  
  19.          <property name="logAbandoned" value="true"/>  
  20.          <property name="removeAbandoned" value="true"/>  
  21.          <property name="removeAbandonedTimeout" value="180"/>  
  22.     </bean>  
  23.       
  24.     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
  25.         <property name="configLocation" value="classpath:/resource/cfg.xml"/>  
  26.         <property name="dataSource" ref="dataSource"/>  
  27.     </bean>  
  28.           
  29.     <bean id="personDao" class="org.mybatis.spring.mapper.MapperFactoryBean">  
  30.         <property name="mapperInterface" value="net.itaem.dao.PersonDao"/>  
  31.         <property name="sqlSessionFactory" ref="sqlSessionFactory"/>  
  32.     </bean>  
  33. </beans>  
<?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-3.0.xsd">
	 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
	     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	     <property name="url" value="jdbc:mysql://localhost:3306/test"/>
	     <property name="username" value="root"/>
	     <property name="password" value="123abc"/>
	     <!-- 连接池启动时候的初始连接数 -->
	     <property name="initialSize" value="10"/>
	     <!-- 最小空闲值 -->
	     <property name="minIdle" value="5"/>
	     <!-- 最大空闲值 -->
	     <property name="maxIdle" value="20"/>
	     <property name="maxWait" value="2000"/>
	     <!-- 连接池最大值 -->
	     <property name="maxActive" value="50"/>
	     <property name="logAbandoned" value="true"/>
	     <property name="removeAbandoned" value="true"/>
	     <property name="removeAbandonedTimeout" value="180"/>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="classpath:/resource/cfg.xml"/>
		<property name="dataSource" ref="dataSource"/>
	</bean>
		
	<bean id="personDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="net.itaem.dao.PersonDao"/>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean>
</beans>
            看到上面配置personDao,我们单独的使用一个Bean去创建它,也许我们会想到如果我们又几十甚至是几百个dao,那配置就可能很恐怖了,不过,Spring的作者也考虑到了这样的问题,针对这种情况,如果dao很多的情况,其实也是可以采取扫描dao的形式注入spring的,具体怎么实现,我们下面会继续介绍。

创建MyBatis配置文件

           mybatis的配置文件除了去掉environment标签,其他没啥区别。
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <!DOCTYPE configuration    
  3.   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"    
  4.   "http://mybatis.org/dtd/mybatis-3-config.dtd">    
  5. <configuration>    
  6.     <!-- mapping 文件路径配置 -->    
  7.     <mappers>    
  8.         <mapper resource="resource/PersonMapper.xml" />    
  9.     </mappers>    
  10. </configuration>  
<?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>  
    <!-- mapping 文件路径配置 -->  
    <mappers>  
        <mapper resource="resource/PersonMapper.xml" />  
    </mappers>  
</configuration>

创建映射文件

            映射文件和之前的映射文件保持一致。
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd" >  
  3. <mapper namespace="net.itaem.dao.PersonDao" >  
  4.     
  5.   <resultMap id="resultMap" type="net.itaem.po.Person" >  
  6.         <result column="id" property="id" jdbcType="CHAR" />  
  7.         <result column="name" property="name" jdbcType="CHAR" />  
  8.   </resultMap>  
  9. <!--添加-->   
  10.  <insert id="save"  parameterType="net.itaem.po.Person">  
  11.  insert into person(id,name) value(#{id,jdbcType=CHAR},#{name,jdbcType=CHAR})  
  12.  </insert>  
  13. <!--查询-->  
  14.  <select id="query"  resultMap="resultMap">  
  15.  select * from person  
  16.  </select>  
  17.  <!--删除-->  
  18. <delete id="delete" parameterType="java.lang.String">  
  19.  delete from person  where id=#{id,jdbcType=CHAR}  
  20. </delete>  
  21. </mapper>  
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="net.itaem.dao.PersonDao" >
  
  <resultMap id="resultMap" type="net.itaem.po.Person" >
	    <result column="id" property="id" jdbcType="CHAR" />
	    <result column="name" property="name" jdbcType="CHAR" />
  </resultMap>
<!--添加--> 
 <insert id="save"  parameterType="net.itaem.po.Person">
 insert into person(id,name) value(#{id,jdbcType=CHAR},#{name,jdbcType=CHAR})
 </insert>
<!--查询-->
 <select id="query"  resultMap="resultMap">
 select * from person
 </select>
 <!--删除-->
<delete id="delete" parameterType="java.lang.String">
 delete from person  where id=#{id,jdbcType=CHAR}
</delete>
</mapper>

Spring使用MyBatis测试

         下面,让我先看看spring的测试用例中如何使用mybatis。
  1. /** 
  2.  * @author: Fighter168 
  3.  */  
  4. public class SpringTest {  
  5.   
  6.     public static void main(String[] args) {  
  7.         ApplicationContext context=new ClassPathXmlApplicationContext("resource/ApplicationContext.xml");  
  8.         PersonDao personDao=(PersonDao) context.getBean("personDao");  
  9.         Person person=new Person("12","Fighter168");  
  10.         personDao.save(person);  
  11.     }  
  12. }  
/**
 * @author: Fighter168
 */
public class SpringTest {

	public static void main(String[] args) {
		ApplicationContext context=new ClassPathXmlApplicationContext("resource/ApplicationContext.xml");
		PersonDao personDao=(PersonDao) context.getBean("personDao");
		Person person=new Person("12","Fighter168");
		personDao.save(person);
	}
}
         通过上面的比较,我们发现,在Spring中使用MyBatis是相当方便的,不需要我们去管理sqlSessionFactory以及SqlSession的管理,更不需要我们去操作简单的事务。


          至于Spring到底做了上面工作,下面我们进入主题吧,一探究竟。


spring整合mybatis实现


SqlSessionFactoryBean的创建

            SqlSessionFactoryBean这个类实现了三个接口,一个是InitializingBean,另一个是FactoryBean,还有就是ApplicationListener接口。下面说明一下实现了这三个接口的,有什么作用

InitializingBean接口:实现了这个接口,那么当bean初始化的时候,spring就会调用该接口的实现类的afterPropertiesSet方法,去实现当spring初始化该Bean 的时候所需要的逻辑。

FactoryBean接口:实现了该接口的类,在调用getBean的时候会返回该工厂返回的实例对象,也就是再调一次getObject方法返回工厂的实例。

ApplicationListener接口:实现了该接口,如果注册了该监听的话,那么就可以了监听到Spring的一些事件,然后做相应的处理

SqlSessionFactoryBean的初始化
              SqlSessionFactory的初始化是在Spring初始化该Bean 的时候就会初始化,实现InitializingBean接口的目的就是为了这个,让我们看看SqlSessionFactoryBean中实现InitializingBean的afterPropertiesSet方法。
           
  1. /** 
  2.  * {@inheritDoc} 
  3.  */  
  4. public void afterPropertiesSet() throws Exception {  
  5.   notNull(dataSource, "Property 'dataSource' is required");  
  6.   notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");  
  7.   
  8.   this.sqlSessionFactory = buildSqlSessionFactory();  
  9. }  
  /**
   * {@inheritDoc}
   */
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

          从中我们可以看到,sqlSessionFactory的实例化便在这个方法里面实例化,buildSqlSessionFactory()方法会对我们的sqlSessionFactory做定制的初始化,初始化sqlSessionFactory有两种方式,一种是我们直接通过property直接注入到改实例中,另一种是通过解析xml的方式,就是我们在configuration.xml里面的配置,根据这些配置做了相应的初始化操作,里面也是一些标签的解析属性的获取,操作,和Spring的默认标签解析有点类似,这里就不再重复说明。

获取SqlSessionFactoryBean实例
           因为SqlSessionFactoryBean实现了FactoryBean接口,所以当我们通过getBean获取它的实例的时候实际是调用他的getObject方法,获取到的是sqlSessionFactory。

  1. /** 
  2.  * {@inheritDoc} 
  3.  */  
  4. public SqlSessionFactory getObject() throws Exception {  
  5.   if (this.sqlSessionFactory == null) {  
  6.     afterPropertiesSet();  
  7.   }  
  8.   
  9.   return this.sqlSessionFactory;  
  10. }  
  /**
   * {@inheritDoc}
   */
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
        所以我们在给dao注入sqlSessionFactory的时候,依赖填写SqlSessionFactoryBean 的实例就可以了。


MapperFactoryBean的创建

       在使用mybatis的时候,我们获取dao的方式一般是这样:
  1. SqlSession session=sessionFactory.openSession();  
  2. PersonDao personDao=session.getMapper(PersonDao.class);  
		SqlSession session=sessionFactory.openSession();
		PersonDao personDao=session.getMapper(PersonDao.class);
      但在我们在spring的测试用例中使用mybatis的时候是这样使用的:
  1. PersonDao personDao=(PersonDao) context.getBean("personDao");  
                PersonDao personDao=(PersonDao) context.getBean("personDao");
    为什么spring可以这样做呢,答案就在MapperFactoryBean这里

让我先来看看这个类的类层次结构图:


MapperFactoryBean也实现了FactoryBean和InitializingBean接口,我们也从MapperFactoryBean的初始化开始吧,看看它如何初始化。

MapperFactoryBean初始化
MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport继承DaoSupport,DaoSupport实现了InitializingBean接口,让我们开看看它这接口的实现:

  1. public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {  
  2.     // Let abstract subclasses check their configuration.  
  3.     checkDaoConfig();  
  4.   
  5.     // Let concrete implementations initialize themselves.  
  6.     try {  
  7.         initDao();  
  8.     }  
  9.     catch (Exception ex) {  
  10.         throw new BeanInitializationException("Initialization of DAO failed", ex);  
  11.     }  
  12. }  
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// Let abstract subclasses check their configuration.
		checkDaoConfig();

		// Let concrete implementations initialize themselves.
		try {
			initDao();
		}
		catch (Exception ex) {
			throw new BeanInitializationException("Initialization of DAO failed", ex);
		}
	}

该方法主要包含两个功能,一个是调用checkDaoConfig()方法,一个是调用initDao方法。checkDaoConfig方法在DaoSupport是抽象方法,让我看看它在MapperFactoryBean的实现:
  1. /** 
  2.   * {@inheritDoc} 
  3.   */  
  4.  @Override  
  5.  protected void checkDaoConfig() {  
  6.    super.checkDaoConfig();  
  7.   
  8.    notNull(this.mapperInterface, "Property 'mapperInterface' is required");  
  9.   
  10.    Configuration configuration = getSqlSession().getConfiguration();  
  11.    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {  
  12.      try {  
  13.        configuration.addMapper(this.mapperInterface);  
  14.      } catch (Throwable t) {  
  15.        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);  
  16.        throw new IllegalArgumentException(t);  
  17.      } finally {  
  18.        ErrorContext.instance().reset();  
  19.      }  
  20.    }  
  21.  }  
 /**
   * {@inheritDoc}
   */
  @Override
  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.addMapper(this.mapperInterface);
      } catch (Throwable t) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
        throw new IllegalArgumentException(t);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

             该方法主要是检查dao的配置,主要是检验sqlSessionFactory和mapperInterface属性不能为空,以及检测接口对于的映射文件是否存在,如果存在,那么就把它添加到configuration里面去,注册mapper。

获取MapperFactoryBean的实例
           MapperFactoryBean实现了FactoryBean接口,那么在调用getBean方法获取MapperFactoryBean实例的时候,实际上调用的就是getObject方法,让我们来看看getObject的实现:
  1. /** 
  2.  * {@inheritDoc} 
  3.  */  
  4. public T getObject() throws Exception {  
  5.   return getSqlSession().getMapper(this.mapperInterface);  
  6. }  
  /**
   * {@inheritDoc}
   */
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
          
        看到这里,我们会恍然大悟,原来在这里封装了getMapper操作,返回接口的实例,怪不得在Spring中使用MyBatis不用管理sqlSession了。
        所以对于上面的测试用例,Spring怎么封装了MyBatis,如何把sqlSessionFactory和sqlSession隐藏了起来,又怎么方便的获取dao接口实例,我们大概有了一个了解。
        
        那么让我在回到之前提到的那个问题,如果有成百上千个dao接口呢,那我们岂不是要配置添加成百上千个bean,当然不是这样,spring还为MyBatis添加了拓展的功能,可以通过扫描包目录的方式,添加dao,让我看看具体使用和实现。




MapperScannerConfigurer介绍


         如果我们的dao在一个包下面又好几十个,那么我可以可以通过扫描的方式添加dao,像下面一样使用

  1. <!-- 去掉该配置  
  2. <bean id="personDao" class="org.mybatis.spring.mapper.MapperFactoryBean">  
  3.     <property name="mapperInterface" value="net.itaem.dao.PersonDao"/>  
  4.     <property name="sqlSessionFactory" ref="sqlSessionFactory"/>  
  5. </bean>  
  6.  -->  
  7.  <!-- 如果 net.itaem.dao 包下面有很多dao需要注册,那么可以使用这种扫描的方式添加dao-->  
  8. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
  9.     <property name="basePackage" value="net.itaem.dao"/>  
  10. </bean>  
	<!-- 去掉该配置
	<bean id="personDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="net.itaem.dao.PersonDao"/>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean>
	 -->
	 <!-- 如果 net.itaem.dao 包下面有很多dao需要注册,那么可以使用这种扫描的方式添加dao-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="net.itaem.dao"/>
	</bean>

           看到上面的配置,我们会很好奇,在spring这样添加就可以扫描的方式添加dao配置,怎么做到的?让我打开类实现,具体看一下。


MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,如果MapperScannerConfigurer实现了该接口,那么说明在application初始化的时候该接口会被调用,具体实现,让我先看看:

  1. /** 
  2.  * {@inheritDoc} 
  3.  *  
  4.  * @since 1.0.2 
  5.  */  
  6. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  
  7.   if (this.processPropertyPlaceHolders) {  
  8.     processPropertyPlaceHolders();  
  9.   }  
  10.   
  11.   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);  
  12.   scanner.setAddToConfig(this.addToConfig);  
  13.   scanner.setAnnotationClass(this.annotationClass);  
  14.   scanner.setMarkerInterface(this.markerInterface);  
  15.   scanner.setSqlSessionFactory(this.sqlSessionFactory);  
  16.   scanner.setSqlSessionTemplate(this.sqlSessionTemplate);  
  17.   scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);  
  18.   scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);  
  19.   scanner.setResourceLoader(this.applicationContext);  
  20.   scanner.setBeanNameGenerator(this.nameGenerator);  
  21.   scanner.registerFilters();  
  22.   scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));  
  23. }  
  /**
   * {@inheritDoc}
   * 
   * @since 1.0.2
   */
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

这里我们重点关注三个主要的方法,分别是
processPropertyPlaceHolders(); 
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));

processPropertyPlaceHolders属性处理

执行属性的处理,简单的说,就是把xml中${XXX}中的XXX替换成属性文件中的相应的值


根据配置属性生成过滤器

scanner.registerFilters();方法会根据配置的属性生成对应的过滤器,然后这些过滤器在扫描的时候会起作用。


扫描java文件

scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));

该方法主要做了以下操作:
1)扫描basePackage下面的java文件
2)解析扫描到的java文件
3)调用各个在上一步骤注册的过滤器,执行相应的方法。
4)为解析后的java注册bean,注册方式采用编码的动态注册实现。
5)构造MapperFactoryBean的属性,mapperInterface,sqlSessionFactory等等,填充到BeanDefinition里面去。

做完这些,MapperFactoryBean对象也就构造完成了,扫描方式添加dao的工作也完成了。



总结

                   其实了解了Spring整合MyBatis的流程,我们也就大体知道Spring整合一些框架所使用的扩展方法,不过大多是都是通过继承接口的方式,然后通过spring回调该接口的方式,实现我们自己想要的扩展逻辑,所以了解spring提供的一些扩展的接口以及抽象类是扩展的关键,就像InitializingBean,BeanDefinitionRegistryPostProcessor这些接口,知道了这些接口调用的方式,以及上面时候会调用,我们就可以知道,我们需要扩展的功能应该实现哪个接口,或者集成哪个抽象类。目前这些接口,官方没有整理出一份比较好的文档,不过在后续的博客中,我会把这些常用的拓展接口以及抽象类都提出来,介绍下,让大家熟悉下这些可以对spring进行扩展的接口以及抽象类。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值