Spring Boot 2.x整合Mybatis原理剖析

Mybatis是目前市面上使用的最多的ORM框架了,在Spring Boot项目中可以非常简单的集成。使用起来很简单,但是具体是怎么样集成的呢?这就来一探究竟!

Mybatis官方文档
mybatis-spring-boot-starter官方文档

使用Mybatis的前提就是构建一个SqlSessionFactory实例。
有两种方式:

  1. 通过读取xml配置
  2. 穿入一个Configuration对象。
    在这里插入图片描述
    而要使用Mybatis操作数据库。就得从SqlSessionFactory中构建SqlSession实例对象。

在这里插入图片描述
在与Spring整合时,使用的SqlSession的实现就是SqlSessionTemplate 。

因此Spring Boot要自动装配MyBatis,其实就是要初始化SqlSessionFactory和SqlSessionTemplate实例

直接看mybatis-spring-boot-autoconfigure\2.1.0\mybatis-spring-boot-autoconfigure-2.1.0.jar!\META-INF\spring.factories文件配置
在Spring Boot启动的时候会自动读取这里面的配置类。

在这里插入图片描述
这里的核心配置类是org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

在这里插入图片描述
可以看到,确实在这个自动配置类中会初始化SqlSessionFactory实例和SqlSessionTemplate实例

自定义MyBatis配置:
① 新建一个类实现ConfigurationCustomizer接口。
② 加上@org.springframework.context.annotation.Configuration

下面继续通过源码来分析如何初始化的:

1. 初始化,从spring容器中加载一下实例

  public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
      ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
      ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    //mybatis配置  
    this.properties = properties;
    //拦截器
    this.interceptors = interceptorsProvider.getIfAvailable();
    //类型装换器
    this.typeHandlers = typeHandlersProvider.getIfAvailable();
    //语言驱动
    this.languageDrivers = languageDriversProvider.getIfAvailable();
    //资源加载器
    this.resourceLoader = resourceLoader;
    //数据源ID生产器
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    //自定义的配置,自定义配置类,实现ConfigurationCustomizer接口,在这里会加载自定义配置
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
  }

2. 初始化SqlSessionFactory实例

  /**
   * 如果spring容器中SqlSessionFactory实例,就执行下面方法构造一个
   * 方法参数为DataSource,表示自动注入数据源实例到本方法当中
   *   DataSource实例会在DataSourceAutoConfiguration中构建或者自定义构建
   *  
   **/
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    //构建一个SqlSessionFactory的工厂类SqlSessionFactoryBean 
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    //设置数据源
    factory.setDataSource(dataSource);
    //设置VFS
    factory.setVfs(SpringBootVFS.class);
    //设置Mybatis的xml配置文件 ,如果没有配置就不执行操作,当前默认是没有的
    // Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
    // "WEB-INF/mybatis-configuration.xml".
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    //设置Configuration对象到SqlSessionFactory
    //注意:ConfigLocation和Configuration不能同时存在,当前版本默认使用Configuration
    applyConfiguration(factory);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    //设置拦截器组件
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    //设置数据源ID生成器
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    //设置typeAliasesPackage属性
    //Packages to search type aliases. (Package delimiters are ",; \t\n")
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    //设置typeAliasesSuperType属性
    //The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that
    // searched from typeAliasesPackage.
    if (this.properties.getTypeAliasesSuperType() != null) {
      factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
    }
    //设置typeHandlersPackage
    //Packages to search for type handlers. (Package delimiters are ",; \t\n")
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    //设置TypeHandlers
    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
      factory.setTypeHandlers(this.typeHandlers);
    }
    //设置mapperLocations属性 Locations of MyBatis mapper files.
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    //设置mybatis生产SQL的语言方式
    Set<String> factoryPropertyNames = Stream
        .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
        .collect(Collectors.toSet());
    Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
    if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
      // Need to mybatis-spring 2.0.2+
      factory.setScriptingLanguageDrivers(this.languageDrivers);
      if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
        defaultLanguageDriver = this.languageDrivers[0].getClass();
      }
    }
    if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
      // Need to mybatis-spring 2.0.2+
      factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
    }
    //返回SqlSessionFactory 
    return factory.getObject();
  }

可以到,Spring Boot2.x是默认以FactroyBean的方式来构建SqlSessionFactory实例。下面来看看SqlSessionFactoryBean的具体实现逻辑:

在这里插入图片描述
可以看到,SqlSessionFactoryBean不仅仅是一个FactoryBean对象,还实现了ApplicationListener。

事件的相关实现如下:

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    //在应用刷新时,并且failFast=true时才会check all statements are completed
    if (failFast && event instanceof ContextRefreshedEvent) {
      // fail-fast -> check all statements are completed
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
  

然后,既然是FactoryBean的方式,先看一下FactoryBean的方法:

public interface FactoryBean<T> {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * <p>As with a {@link BeanFactory}, this allows support for both the
	 * Singleton and Prototype design pattern.
	 * <p>If this FactoryBean is not fully initialized yet at the time of
	 * the call (for example because it is involved in a circular reference),
	 * throw a corresponding {@link FactoryBeanNotInitializedException}.
	 * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
	 * objects. The factory will consider this as normal value to be used; it
	 * will not throw a FactoryBeanNotInitializedException in this case anymore.
	 * FactoryBean implementations are encouraged to throw
	 * FactoryBeanNotInitializedException themselves now, as appropriate.
	 * @return an instance of the bean (can be {@code null})
	 * @throws Exception in case of creation errors
	 * @see FactoryBeanNotInitializedException
	 */
	@Nullable
	T getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates,
	 * or {@code null} if not known in advance.
	 */
	@Nullable
	Class<?> getObjectType();

	 /**
	 * Is the object managed by this factory a singleton? That is,
	 * will {@link #getObject()} always return the same object
	 *
	 */
	default boolean isSingleton() {
		return true;
	}

}

最要的是getObject()方法,我们看一下SqlSessionFactoryBean中的实现,可以看到其实最终还是使用sqlSessionFactoryBuilder来构建的SqlSessionFactory

private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

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

  return this.sqlSessionFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
  //检查数据源实例是否存在
  notNull(dataSource, "Property 'dataSource' is required");
  //检查sqlSessionFactoryBuilder实例是否存在,默认肯定是存在的
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  //configuration和configLocation不能同时为null,也不能都不为空
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
      "Property 'configuration' and 'configLocation' can not specified with together");
  //构建 SqlSessionFactory 
  this.sqlSessionFactory = buildSqlSessionFactory();
}

其实核心构建逻辑都在buildSqlSessionFactory()方法中:

/**
 * Build a {@code SqlSessionFactory} instance.
 *
 * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
 * {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a {@link Configuration}
 * instance directly(without config file).
 *
 * @return SqlSessionFactory
 * @throws Exception
 *           if configuration is failed
 */
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

  final Configuration targetConfiguration;

  XMLConfigBuilder xmlConfigBuilder = null;
  //不管是XML配置还是非XML配置,都构建一个Configuration实例 
  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)) {
    scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
        .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
        .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
  }

  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)) {
    scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
        .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
        .filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) != null)
        .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
  }

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

  if (!isEmpty(this.scriptingLanguageDrivers)) {
    Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
      targetConfiguration.getLanguageRegistry().register(languageDriver);
      LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
    });
  }
  Optional.ofNullable(this.defaultScriptingLanguageDriver)
      .ifPresent(targetConfiguration::setDefaultScriptingLanguage);
  //设置数据源的ID 
  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();
    }
  }
  //设置 事物管理器,缺省配置为SpringManagedTransactionFactory()
  targetConfiguration.setEnvironment(new Environment(this.environment,
      this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
      this.dataSource));
  //解析mapper文件
  if (this.mapperLocations != null) {
    if (this.mapperLocations.length == 0) {
      LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
    } else {
      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.");
  }

  return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

这个构造过程就是构建Configuration对象,然后使用new DefaultSqlSessionFactory(config)来实例化SqlSessionFactory实例。

可以看到DefaultSqlSessionFactory作为SqlSessionFactory的默认实现

3. 实例化SqlSessionTemplate
在这里插入图片描述
可以看到SqlSessionTemplate是需要SqlSessionFactory实例去构建的

  @Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }

SqlSessionTemplate是一个执行SQL的模版工具。而数据库操作接口全部在SqlSession接口中。在SqlSessionTemplate内部使用的是SqlSession的代理来执行数据库相关操作。

SqlSessionTemplate的实现的核心使用了Java的动态代理,Java的动态代理只能代理接口类,详情自己去翻译相关资料
在这里插入图片描述
构造需要3个参数
① SqlSessionFactory 必须
② ExecutorType 执行类型 ,非必填,默认ExecutorType.SIMPLE
③ PersistenceExceptionTranslator 异常处理器 , 非必须,默认MyBatisExceptionTranslator

这里默认通过new SqlSessionTemplate(sqlSessionFactory)构造SqlSessionTemplate实例:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
     PersistenceExceptionTranslator exceptionTranslator) {

     notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
     notNull(executorType, "Property 'executorType' is required");

	 this.sqlSessionFactory = sqlSessionFactory;
	 this.executorType = executorType;
	 this.exceptionTranslator = exceptionTranslator;
	 //构建SqlSession动态代理,使用Proxy.newProxyInstance()方法
	 this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
	     new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}

/**
 * 代理执行SqlSession方法的逻辑处理 
 * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
 * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to the
 * {@code PersistenceExceptionTranslator}.
 */
private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //获取SqlSession
    SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
    try {
      //执行相关数据库操作
      Object result = method.invoke(sqlSession, args);
      //如果没有使用事物,就默认提交当前操作
      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) {
      Throwable unwrapped = unwrapThrowable(t);
      if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
        // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        sqlSession = null;
        //异常处理
        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
        if (translated != null) {
          unwrapped = translated;
        }
      }
      throw unwrapped;
    } finally {
      //关闭sqlSession
      if (sqlSession != null) {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
}

获取SqlSession核心逻辑:

/**
   * Gets an SqlSession from Spring Transaction Manager or creates a new one if needed. Tries to get a SqlSession out of
   * current transaction. If there is not any, it creates a new one. Then, it synchronizes the SqlSession with the
   * transaction if Spring TX is active and <code>SpringManagedTransactionFactory</code> is configured as a transaction
   * manager.
   *
   * @param sessionFactory
   *          a MyBatis {@code SqlSessionFactory} to create new sessions
   * @param executorType
   *          The executor type of the SqlSession to create
   * @param exceptionTranslator
   *          Optional. Translates SqlSession.commit() exceptions to Spring exceptions.
   * @return an SqlSession managed by Spring Transaction Manager
   * @throws TransientDataAccessResourceException
   *           if a transaction is active and the {@code SqlSessionFactory} is not using a
   *           {@code SpringManagedTransactionFactory}
   * @see SpringManagedTransactionFactory
   */
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //从事物管理器中获取sqlSession包装类SqlSessionHolder ,sessionFactory作为缓存的key
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //从SessionHolder中解析出SqlSession 
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }
    //如果没有,就是用sessionFactory来新建一个SqlSession
    LOGGER.debug(() -> "Creating a new SqlSession");
    session = sessionFactory.openSession(executorType);
    
    //缓存到事物管理器中
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

在SessionFactory.openSession(executorType)时,在新建Executor之前会执行自定义的Interceptor链 。

到这里SqlSessionTemplate就初始化完成,其他详细的技术细节篇幅有限这里就不分析了。

  1. 添加Mapper接口扫描类
/**
   * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
   * mappers based on the same component-scanning path as Spring Boot itself.
   */
  @org.springframework.context.annotation.Configuration
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }

  }

核心逻辑在AutoConfiguredMapperScannerRegistrar这里,详细原理参考@Import注解的使用。作用就是想spring容器中注册自定义的类。这里也是一样。主要目的是注册MapperScannerConfigurer实例到spring容器中。MapperScannerConfigurer的作用是扫描处理指定包下所有的标注有@Mapper的Mapper接口,然后注册到spring容器中。

 /**
   * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use
   * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,
   * similar to using Spring Data JPA repositories.
   */
  public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {

    private BeanFactory beanFactory;
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      //Determine if the auto-configuration base packages for the given bean factory are available.
      if (!AutoConfigurationPackages.has(this.beanFactory)) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
        return;
      }

      logger.debug("Searching for mappers annotated with @Mapper");
      //获取要扫描的包,没有指定的话就是spring启动类所在的包
      List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
      if (logger.isDebugEnabled()) {
        packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
      }
      //构建MapperScannerConfigurer的BeanDefinition(bean定义对象)
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
      //给bean的属性赋值
      builder.addPropertyValue("processPropertyPlaceHolders", true);
      builder.addPropertyValue("annotationClass", Mapper.class);
      builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
      //查找MapperScannerConfigurerbean中是否存在lazyInitialization属性,如果有就取配置中mybatis.lazy-initialization的值
      //如果mybatis.lazy-initialization没有指定,默认为false
      BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
      Stream.of(beanWrapper.getPropertyDescriptors())
          // Need to mybatis-spring 2.0.2+
          .filter(x -> x.getName().equals("lazyInitialization")).findAny()
          .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
      //注册到spring容器中    
      registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    }
    
    //注入BeanFactory实例对象
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
      this.beanFactory = beanFactory;
    }

  }

自定义Mapper接口有2种方式
1 在自定的Mapper接口上加上@Mapper注解
2 Mapper接口不用加注解,使用mybatis.type-aliases-package=Mapper接口所在包 来指定要扫描的Mapper接口所在包

到此。Spring Boot2整合Mybatis的全逻辑就分析清楚了。关于自定义Mapper操作数据的详细过程。在MyBatis的原理解析中我再细细分析。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值