7-mybatis深入学习-源码学习之spring与mybatis整合

一、spring 与mybaits集成

1、这里主要讲通过配置方式来集成,主要在以下几个方面体现:

  1. Spring 将会加载必要的 MyBatis 工厂类和 session 类;
  2. 提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean
  3. 方便集成 spring 事务;
  4. 翻译 MyBatis 的异常到 Spring 的 DataAccessException 异常(数据访问异常)中;

1、加入配置文件

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="typeAliasesPackage" value="com.enjoylearning.mybatis.entity" />
		//配置 SqlSessionFactoryBean 扫描 XML 映射文件的路径, 可以使用 Ant 风格的路径进行配置。
		<property name="mapperLocations" value="classpath:sqlmapper/*.xml" />
</bean>

!-- DAO接口所在包名,Spring会自动查找其下的类 -->
//通过 MapperScannerConfigurer 类自动扫描所有的 Mapper 接口,使用时可以直接注入接口。
 	 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.enjoylearning.mybatis.mapper" />
	</bean>
<!-- (事务管理)transaction manager -->
//配置事务,让 Mybatis 集成 spring 的事务
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
		<qualifier value="transactionManager" />
	</bean>

	<tx:annotation-driven transaction-manager="transactionManager" />

二、MyBatis-Spring 集成原理分析

(1)、SqlSessionFactoryBean 源码分析

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
    private Resource configLocation;
   private Configuration configuration;
 }

SqlSessionFactoryBean 实现了 InitializingBean 接口, 那么容器在初始化完成 SqlSessionFactoryBean 之后必然会调用 afterPropertiesSet()方法,如下图所示,其中调用的 buildSqlSessionFactory()方法实际是对 MyBatis 初始化加载配置阶段的封装;

 @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

SqlSessionFactoryBean 实现了 FactoryBean接口, 实出了getObject方法,将SqlSessionFactory 注入到IOC:

@Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }

这样就将sqlSessionFactory对象注入到了IOC容器,IOC 容器中的其他类型能拿到 SqlSession 实例了,就可以进行相关的 SQL 执行任务了;

(2)MapperScannerConfigurer源码分析:

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
  private String basePackage;
  private boolean addToConfig = true;
  private SqlSessionFactory sqlSessionFactory;
  private SqlSessionTemplate sqlSessionTemplate;
  private String sqlSessionFactoryBeanName;
  ......
  }

这个类继承了BeanDefinitionRegistryPostProcessor接口,这个类是IOC容器后置处理器,在注入完bean定义后,会执行实现这个接口的类方法postProcessBeanDefinitionRegistry,这个方法扫描完这些 mapper 接口之后,主要是将 Mapper 接口一个个的转换成 MapperFactoryBean 之后注入IOC容器。
查看postProcessBeanDefinitionRegistry方法:

@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    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));
  }

查看 ClassPathMapperScanner .scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));方法:

@Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

processBeanDefinitions方法将Mapper 接口一个个的转换成 MapperFactoryBean 之后注入容器

(3)MapperFactoryBean ,getObject最终注入不同代理对象

在这里插入图片描述
可以看到MapperFactoryBean实现了FactoryBean接口,所以最终注入的是getObject方法返回的对象

public T getObject() throws Exception {
   return this.getSqlSession().getMapper(this.mapperInterface);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

public T newInstance(SqlSession sqlSession) {
   MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值