Mybatis源码学习第十课---Mybatis与spring集成源码

本文详细探讨了Mybatis与Spring的集成配置,包括SqlSessionFactoryBean的源码分析,解释了如何配置数据源、扫描Mapper接口。同时,深入解析了SqlSessionTemplate的工作原理,以及MapperFactoryBean如何创建和管理Mapper接口的动态代理对象,最后总结了Mybatis-Spring集成的关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一 、集成配置

二 、SqlSessionFactoryBean源码分析

三   SqlSessionTemplate 

四 、MapperFactoryBean

4.1 MapperFactoryBean测试

4.2 MapperFactoryBean源码解析

2.2.1 这里解决了一个重要问题

五、MapperScannerConfigurer

六.总结

参考


一 、集成配置

   在pom.xml文件中添加MyBatis-Spring的依赖

<!-- mybatis与spring对接依赖 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.3</version>
		</dependency>

  配置SqlSessionFactoryBean

     applicationContext.xml进行配置。

<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="typeAliasesPackage" value="com.enjoylearning.mybatis.entity" />
		<property name="mapperLocations" value="classpath:sqlmapper/*.xml" />
		<property name="configLocation" value="mybatis-config.xml"/>
	</bean>

在MyBatis-Spring中,SqlSessiongFacotyBean是用于创建SqlSessionFactory的。

  • dataSource:用于配置数据源,该属性为必选项,必须通过这个属性配置数据源
  • mapperLocations:配置SqlSessionFactoryBean扫描XML映射文件的路径,可以使用Ant风格的路径进行配置
  • configLocation:用于配置mybatis config XML的路径,除了数据源外,对MyBatis的各种配置仍然可以通过这种方式进行,并且配置MyBatis settings时只能使用这种方式。但配置文件中任意环境、数据源和MyBatis的任务管理器都会被忽略。
  • typeAliasesPackage:配置包中类的别名,配置后,包中的类在XML映射文件中使用时可以省略包名部分,直接使用类名。这个配置不支持Ant风格的路径,当需要配置多个包路径时可以使用分号或逗号进行分隔。

配置MapperScannerConfigurer或者扫描包

 <bean id="tUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="com.enjoylearning.mybatis.mapper.TUserMapper"></property>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean> 
	
	<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
	 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.enjoylearning.mybatis.mapper" />
	</bean> 

通过MapperScannerConfigurer类自动扫描所有的Mapper接口,使用时可以直接注入接口。
MapperScannerConfigurer中常配置以下两个属性:

  • basePackage:用于配置基本的包路径。可以使用分号或逗号作为分隔符设置多于一个的包路径,每个映射器将会在指定的包路径中递归地被搜索到。
  • annotationClass:用于过滤被扫描的接口,如果设置了该属性,那么MyBatis的接口只有包含注解才会被扫描进去。

二 、SqlSessionFactoryBean源码分析

    MyBatis 初始化过程时提到,SqlSessionFactoryBuilder会通过XMLConfigBuilder 等对象读取 mybatis-config.xml 配置文件以及映射配置信息,得到 Configuration 对象,然后创建 Sq!SessionFactory 对象。而在与 spring 集成时, MyBatis 中的SqlSessionFactory 对象则是由 Sq!SqlSessionFactoryBuilder创建的。

实现FactoryBean接口的getObject方法:

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

 /**
   * 
   * 将SqlSessionFactory对象注入spring容器
   * {@inheritDoc}
   */
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

SqlSessionFactoryBean实现InitializingBean接口,需要实现其afterPropertiesSet():

 @Override
  //在spring容器中创建全局唯一的sqlSessionFactory
  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是如何创建 SqlSessionFactory对象的,该功能是在SqlSessionFactoryBean.buildSqlSessionFactory()方法中实现的,其中涉及使用 XMLConfigBuiIder 创建 Configuration 对象、对 Configuration 对象进行配置、使用 XMLMapperBuilder 解析映射配。核心是buildSqlSessionFactory:

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {//如果configuration不为空,则使用该对象,并对其进行配置
      configuration = this.configuration;
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {//创建xmlConfigBuilder,读取mybatis的核心配置文件
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {//如果configuration为空,实例化一个configuration对象
      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);
      }
    }
    //设置objectFactory
    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }
    //设置objectWrapperFactory
    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }
  //设置vfs
    if (this.vfs != null) {
      configuration.setVfsImpl(this.vfs);
    }
  //扫描指定的包typeAliasesPackage,注册别名
    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");
        }
      }
    }
  //为typeAliases指定的类注册别名
    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }
    //注册插件
    if (!isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered plugin: '" + plugin + "'");
        }
      }
    }
    //扫描指定的包typeHandlersPackage,注册类型解析器
    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
        }
      }
    }
  //为typeHandlers指定的类注册类型解析器
    if (!isEmpty(this.typeHandlers)) {
      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type handler: '" + typeHandler + "'");
        }
      }
    }
    //配置databaseIdProvider
    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
      try {
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }
  //配置缓存
    if (this.cache != null) {
      configuration.addCache(this.cache);
    }
     //使用xmlConfigBuilder读取mybatis的核心配置文件
    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (LOGGER.isDebugEnabled()) {
          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();
      }
    }
    //默认使用SpringManagedTransactionFactory作为事务管理器
    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }
   //设置Environment
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

  //根据mapperLocations的配置,处理映射配置文件以及相应的mapper接口
    if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }
    //最终使用sqlSessionFactoryBuilder创建sqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

三   SqlSessionTemplate 

      SqlSessionTemplate 是 MyBatis-Spring 的核心,它实现了 SqlSession 接口,在 MyBatis 与 Spring 集成开发时,用来代替 MyBatis 中的 DefaultSqlSession 的功能,所以可以通过 SqlSessionTemplate 对象完成指定的数据库操作。 SqlSessionTemplate 是线程安全的,可以在 DAO (Data Access Object,数据访问对象)之间共享使用,其底层封装了 Spring 管理的 SqlSession 对象。

  SqlSessionTemplate核心字段:

 //用于创建SqlSession对象工厂类
  private final SqlSessionFactory sqlSessionFactory;

  //SqlSession 底层使用的 Executor 类型
  private final ExecutorType executorType;
  //通过 JDK 动态代理生成的代理对象
  private final SqlSession sqlSessionProxy;

在 SqlSessionTemplate 的构造方法中会初始化上述字段, 具体实现如下:

 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {
    //.检测 sqlSessionFactory 和 executorType 两个参数不为空(略)
    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //通过 JDK 动态代理的方式,创建 SqlSess工on 类型的代理对象,并初始化 sqlSessionProxy 字段
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

    SqlSessionTemplate 通过调用 sqlSessionProxy 的相应方法实现了 SqlSession 接口 的所有方法, 这里重点来分析创建代理对象时使用的 InvocationHandler 接口实现一-SqlSessionlnterceptor, 其 invoke()方法实现如下:

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //通过静态方法 SqlSessionUtils . getSession ()获取 SqlSession 对象
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        //检测事务是否由 Spring 进行管理,并据此决定是否提交事务
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;//返回结果
      } catch (Throwable t) {
       
      }
    }
  }

在 SqlSessionUtils.getSession()方法中, 会首先尝试从 Spring 事务管理器中获取 SqISession 对象,如果获取成功则直接返回,否则通过 SqlSessionFactory 新建 SqlSession 对象井将其交由 Spring 事务管理器管理后返回, 具体实现如下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //从 Spring 事务管理器中获取 SqlSessionHolder,其中封装了 SqlSession 对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
     //获取SqlSessionHolder 中封装的 SqlSession 对象
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }
   //若上述 SqlSession 为空,则通过 SessionFactory 创建新的 SqlSession 对象
    session = sessionFactory.openSession(executorType);
    //将 SqlSession 对象与 Spring 事务管理器绑定
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

四 、MapperFactoryBean

4.1 MapperFactoryBean测试

applicationContext.xml配置,注入MapperFactoryBean:

    <bean id="tUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.enjoylearning.mybatis.mapper.TUserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>

UserServiceImpl.java中按名字注入tUserMapper:

@Service
public class UserServiceImpl  implements UserService{
    @Resource(name="tUserMapper")
    private TUserMapper userMapper;

    @Override
    public TUser getUserById(Integer id) {
        return userMapper.selectByPrimaryKey(id);
    }
}

测试代码:

public class MybatisSpringTest {
    @Resource
    private UserService us;

    @Test
    public void TestSpringMyBatis(){
        System.out.println(us.getUserById(1).toString());
    }

测试结果:

TUser [id=1, userName=lison, realName=李小宇, sex=1, mobile=186995587422, email=lison@qq.com, note=lison的备注, positionId=1]

4.2 MapperFactoryBean源码解析

实现FactoryBean接口的getObject方法:

  /**
   * 通过在容器中的mapperRegistry,返回当前mapper接口的动态代理
   * 
   * {@inheritDoc}
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

调用SqlSessionTemplate的getMapper:

  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

调用Configuration的getMapper:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

调用MapperRegistry的getMapper:

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

调用MapperProxyFactory的newInstance:

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

2.2.1 这里解决了一个重要问题

    @Test
    // 测试自动映射以及下划线自动转化驼峰
    public void quickStart() throws IOException {
        // 2.获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.获取对应mapper
        TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
        // 4.执行查询语句并返回结果
        TUser user = mapper.selectByPrimaryKey(1);
        System.out.println(user.toString());

    }

TUserMapper是方法级别的,而注入到Spring的容器中显然不是。

@Service
public class UserServiceImpl  implements UserService{
    @Resource(name="tUserMapper")
    private TUserMapper userMapper;

    @Override
    public TUser getUserById(Integer id) {
        return userMapper.selectByPrimaryKey(id);
    }
}

    MyBatis-Spring是通过FactoryBean来解决的,FactoryBean每次通过getObject来生成新的动态代理对象来解决这个问题,本质上与MyBatis中没有区别!

五、MapperScannerConfigurer

MapperScannerConfigurer会将指定basePackage下面的所有接口都以MapperFactoryBean形式注册到Spring容器中。

    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.enjoylearning.mybatis.mapper" />
    </bean>

 

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

    核心实现了BeanDefinitionRegistry后置处理器BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry,主要是对bean的定义信息进行修改增强,例如对bean的类型进行转换:

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {//占位符处理
      processPropertyPlaceHolders();
    }
    //实例化ClassPathMapperScanner,并对scanner相关属性进行配置
    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();//根据上述配置,生成过滤器,只扫描合条件的class
    //扫描指定的包以及其子包
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

调用ClassPathBeanDefinitionScanner:

    public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

        doScan(basePackages);

        // Register annotation config processors, if necessary.
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    }

调用ClassPathMapperScanner的doScan:

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //通过父类的扫描,获取所有复合条件的BeanDefinitionHolder对象
    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 {
      //处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

转换成MapperFactoryBean:

  //处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器
  private void processBeanDefinitions(Set<BeanDefinitionHolder> 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.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      //讲bean的类型转换成mapperFactoryBean
      definition.setBeanClass(this.mapperFactoryBean.getClass());
      //增加addToConfig属性
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      //增加sqlSessionFactory属性
      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;
      }
      //增加sqlSessionTemplate属性
      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;
      }

      //修改自动注入的方式 bytype
      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

六.总结

MyBatis集成到Spring后,只有两件事稍微有不同:

  • 1)Cofiguration的实例化是读取applicationContext.xml,而不是mybatis-config.xml
  • 2)mapper对象是方法级别的,Spring通过FactoryBean进行增强巧妙地解决了这一问题

参考

  • 1)享学课堂Lison老师笔记

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值