Spring整合MyBatis原理分析

前言

原生MyBatis的调用流程如下

    @Test
    public void TestExample() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {

            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            Employee employee = mapper.selectByPrimaryKey(1)
            System.out.println(employee);
        } finally {
            session.close();
        }
    }

那么 当MyBatis被Spring托管之后,对于MyBatis的使用上会不会发生变化呢?

本篇文章主要围绕SqlSessionFactory、SqlSession以及Mapper的创建和使用来分析。

SqlSessionFactory是怎么被创建的?
SqlSessionFactoryBean

这个类是由Spring提供的,类的描述信息大意是讲 这个类可以创建MyBatis SqlSessionFactory对象。

先看下这个类的声明

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

发现其实现了FactoryBean接口,Bean初始化接口、以及事件监听接口

FactoryBean 是一个工厂Bean,可以生成Bean,其逻辑在getObject()

InitializingBean Bean生命周期接口,afterPropertiesSet()会被重新 并在Bean初始化时 被调用

ApplicationListener事件监听接口,可以监听感兴趣的事件 做处理

因此我们首先可以推断 无论是采用xml方式或基于Java Config方式 该类在使用时 都要被加载到IOC容器,成为被Spring管理的Bean。

为了方便理解 我们给出xml和java config两种配置

<!-- 在Spring启动时创建 sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
        <property name="dataSource" ref="dataSource"/>
    </bean>

java config配置

    @Bean
    @Autowired
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setConfigLocation("classpath:mybatis-config.xml")
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*.xml"));
        return sessionFactory.getObject();
    }

SqlSessionFactory的创建在getObject(),先看下这个方法

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

    return this.sqlSessionFactory;
  }
// 实现InitializingBean接口,重写该方法
@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();
  }

buildSqlSessionFactory方法

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

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

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      Stream.of(typeHandlersPackageArray).forEach(packageToScan -> {
        targetConfiguration.getTypeHandlerRegistry().register(packageToScan);
        LOGGER.debug(() -> "Scanned package: '" + packageToScan + "' for type handlers");
      });
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }
        LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified or no matching resources found");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);

buildSqlSessionFactory方法主要做以下几件事:

1、如果mybatis全局配置文件存在,则解析它。并且如果SqlSessionFactoryBean配置了typeAlias、typeHandler、插件plugins等 则解析它

2、如果mapper文件存在 则解析mapper文件

3、将解析后的数据放到Configuration对象中

4、根据SqlSessionFactoryBuilder创建SqlSessionFactory


谁替代了SqlSession?

我们知道 SqlSessionFactory有了,离调用mapper方法 就只差SqlSession了,我们沿着SqlSession这个接口开始找,发现在mybatis-spring包下 有一个新的实现 SqlSessionTemplate ,看到这个类马上想到了 就是它。为什么呢? 因为它实现了SqlSession接口,以前DefaultSqlSession可以做的事情,它都可以做。

我们结合类图再看下

在这里插入图片描述

通过类描述 我们发现 SqlSessionTemplate是线程安全的,可以被所有的DAO共享。没错,这很好的弥补了DefaultSqlSession 线程不安全的弊端。

那我们来使用下SqlSessionTemplate(注释中 给出说明 创建SqlSessionTemplate 须传入sqlSessionFactory)

xml方式

 <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg  ref="sqlSessionFactory"></constructor-arg>
        <constructor-arg  value="SIMPLE"></constructor-arg>
    </bean>

或者

@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
  return new SqlSessionTemplate(sqlSessionFactory,ExecutorType.SIMPLE);
}

单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class DaoSupportTest {

    @Autowired
    private SqlSessionTemplate sqlSession;

    @Test
    public void EmployeeDaoSupportTest() {
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        System.out.println(mapper.selectByPrimaryKey(1));
      System.out.println((Employee)sqlSession.selectOne("com.wojiushiwo.EmployeeMapper.selectByPrimaryKey", 1));
    }

}

附上applicationContext.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">


    <bean id="jdbc" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath*:jdbc.properties"/>
    </bean>

    <!-- Spring的连接池 -->
   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

    <!-- 在Spring启动时创建 sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置一个可以执行批量的sqlSession,全局唯一,单例 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg  ref="sqlSessionFactory"></constructor-arg>
        <constructor-arg  value="SIMPLE"></constructor-arg>
    </bean>
</beans>

说到SqlSessionTemplate, 这里顺便提一下SqlSessionDaoSupport,它是个抽象类,它的一个成员变量就是SqlSessionTemplate,也提供了获取SqlSession的方法

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSessionTemplate sqlSessionTemplate;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }
  
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
  ...
}

可以看到,只要我们提供了SqlSessionFactory,这个类也可以自动生成SqlSessionTemplate,那么我们也可以基于此 来间接地使用SqlSessionTemplate(前提是IOC容器中已存在SqlSessionFactory)

public  class BaseDao extends SqlSessionDaoSupport implements EmployeeMapper{
    //使用sqlSessionFactory
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }

    public Employee selectByPrimaryKey(String statement,Integer empId) {
        return (Employee)getSqlSession().selectOne(statement,empId);
    }
我可以直接使用Mapper接口吗?

看到这里,不免又有些疑惑了,实际生产环境中 不是这样去访问mybatis的呀,生产环境中是直接注入的Mapper接口,形如

public class EmployeeService{
  @Autowired
  private EmployeeMapper employeeMapper;
  ...
}

这又是怎么实现的呢? 别急,继续往下看(这里先以xml配置方式介绍下)

有一个类MapperFactoryBean映入了我们的眼帘,从类名上可以看出 它跟Mapper对象的生成密不可分,同时它也是SqlSessionDaoSupport的子类,自然也就具备了访问Mybatis必备的那些属性和类了

先看下它的类图

在这里插入图片描述

既然是FactoryBean,那么我们就从getObject作为切入点

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    //intentionally empty 
  }
  
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  
  //父类getSqlSession的定义为了方便 就贴这里了
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
  ...
}

看到这,是不是恍然大悟了。getSqlSession().getMapper(this.mapperInterface)这不正是跟我们前面手动调用时 一样嘛!getSqlSession().getMapper(this.mapperInterface)这个方法的主要内容是 生成this.mapperInterface的代理对象,这里不再赘述,可至MyBatis中Mapper是如何产生的?查看原理

我们以MapperFactoryBean来优化下我们前面对EmployeeMapper的调用写法(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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">


    <bean id="jdbc" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath*:jdbc.properties"/>
    </bean>

    <!-- Spring的连接池 -->
   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>
		
    <!-- 在Spring启动时创建 sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
        <property name="dataSource" ref="dataSource"/>
    </bean>
  
		 <!-- 接口的普通注册方式1 -->
    <bean id="employeeMapper" class="org.mybatis.spring.mapper.MapperFactoryBean" parent="baseMapper">
        <property name="mapperInterface" value="com.gupaoedu.crud.dao.EmployeeMapper"/>
    </bean>
  
   <!-- 接口的普通注册方式2 -->
   <bean id="baseMapper" class="org.mybatis.spring.mapper.MapperFactoryBean" abstract="true" lazy-init="true">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
    <bean id="employeeMapper2" parent="baseMapper">
        <property name="mapperInterface" value="com.gupaoedu.crud.dao.EmployeeMapper"/>
    </bean>
</beans>

调用举例

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class DaoSupportTest {

    @Autowired
    private EmployeeMapper employeeMapper2;

    @Test
    public void EmployeeDaoSupportTest() {
        System.out.println(employeeMapper2.selectByPrimaryKey(1));
    }

}
还有更简便地方式使用Mapper接口吗?

虽然上面这种方式 相较于SqlSessionTemplate 便捷了一些,可是 真正的开发场景下 会有很多的Mapper,难道都需要手动将其注册到IOC容器吗?

当然不是了,请继续往下看,mybatis-spring包提供了一个扫描器ClassPathMapperScanner ,可以扫描指定包下的Mapper接口 然后将其注入IOC容器中。

这里我们大致看一下
在这里插入图片描述

ClassPathMapperScanner#doScan

@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;
  }

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
     ...
       //很重要的一步 将BeanClass修改为MapperFactoryBean
      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;
      }

...
  }
}

ClassPathMapperScanner#doScan 主要做两件事:

1、调用父类doScan方法,扫描指定包 获取候选对象 将其注入到IOC容器

2、处理BeanDefinition,最重要的一步是将BeanClass替换为MapperFactoryBean.class

将Mapper作为属性注入到Service等IOC容器,当Service被初始化的过程中,Mapper也会从IOC容器中拿到代理对象,然后就是使用。具体这部分逻辑在FactoryBeanRegistrySupport#doGetObjectFromFactoryBean,这也算是Bean初始化的范畴了,这里不再赘述。

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
			throws BeanCreationException {

		Object object;
		try {
			 ...
				object = factory.getObject();
			...
		}
		catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}
...
		return object;
	}

factory.getObject() 这里又与前面介绍的MapperFactoryBean#getObject调用链吻合了。

至于MyBatis扫描包的配置 也有两种,这里全部给出

基于xml

<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.wojiushiwo.dao"/>
 </bean>

或者

基于@MapperScan,在启动类上指定@MapperScan(value=“com.wojiushiwo.dao”)即可,这里顺便分析下基于@MapperScan注入是如何实现的

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
  ...
}

请注意@Import(MapperScannerRegistrar.class) 注解引入了一个配置类,我们猜测@MapperScan相关的实现全在MapperScannerRegistrar

MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

}

MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,这是一种动态加载Bean的方式,子类会重写registerBeanDefinitions 来处理动态加载Bean的逻辑,与之类似的还有ImportSelector等

registerBeanDefinitions方法中 ClassPathMapperScanner很是眼熟,它不就是我们前面介绍的基于xml方式实现的扫描器吗? 之后的逻辑与前面类似了,不再赘述。

好了,收工。至此 Spring整合MyBatis主要流程分析告一段落。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值