整合逻辑如下:

  • 利用springboot主动拆卸的个性,应用MybatisAutoConfiguration开启mybatis和springboot的整合
  • SqlSessionFactory创立前,尝试读取mybatis前缀的配置文件(如mybatis-spring.xml),记录到SqlSessionFactoryBean#configLocation;

    • 如果未读取到配置文件,间接采纳默认配置创立configuration
    • 如果读取到配置文件,采纳配置信息创立configuration
  • 通过FactoryBean形式创立SqlSessionFactory

    • 默认事务工厂应用SpringManagedTransactionFactory,将来会创立SpringManagedTransaction
  • 创立SqlSessionTemplate操作SqlSession,实质是做了一层代理,目标在于动静的开启和敞开SqlSession——这么做的起因是SqlSession是非线程平安的
  • 在MybatisAutoConfiguration中,通过Scaner扫描@Mapper注解润饰的类,记入beanDefinitionMap中

以下通过源码察看具体实现。

一、主动拆卸

因为springboot主动拆卸的个性,找到mybatis-spring-boot-autoconfigure.jar下的配置文件:

META-INF    |__  spring.factories

文件内容:

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

EnableAutoConfiguration指向了MybatisAutoConfiguration,所以要害配置肯定在这个类。

properties配置

// 导入properties配置@EnableConfigurationProperties({MybatisProperties.class}) // 导入数据源配置@AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean {

进入MybatisProperties类,发现了很多相熟的属性名(typeAliases、typeHandler等)

@ConfigurationProperties( prefix = "mybatis" )public class MybatisProperties {    // 配置文件前缀,比方mybatis-spring.xml    public static final String MYBATIS_PREFIX = "mybatis";    // mapper地址    private String[] mapperLocations;    // typeAliases类型别名包门路    private String typeAliasesPackage;    private Class<?> typeAliasesSuperType;    // typeHandlers类型转换包门路    private String typeHandlersPackage;    private boolean checkConfigLocation = false;    // 执行器枚举:SIMPLE, REUSE, BATCH    private ExecutorType executorType;

二、SqlSessionFactory创立

回忆mybatis独自应用时的步骤:configuration->SqlSessionFactory->sqlSession->getMapper

来看看与springboot整合后的流程。

@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {    // 通过FactoryBean的形式创立SqlSessionFactory    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();    factory.setDataSource(dataSource);    // -- 如果指定了配置文件,则设置配置文件resource    if (StringUtils.hasText(this.properties.getConfigLocation())) {        factory.setConfigLocation(                this.resourceLoader.getResource(this.properties.getConfigLocation()));    }    // -- 如果用户未指定配置文件,则采纳局部默认配置创立configuration    this.applyConfiguration(factory);    if (this.properties.getConfigurationProperties() != null) {        factory.setConfigurationProperties(this.properties.getConfigurationProperties());    }    return factory.getObject();}

spring与三方整合时,多采纳FactoryBean的形式,比方这里的SqlSessionFactoryBean。

察看它的getObject()办法

org.mybatis.spring.SqlSessionFactoryBean#getObjectpublic SqlSessionFactory getObject() throws Exception {    if (this.sqlSessionFactory == null) {        // 间接在afterPropertiesSet里创立了sqlSessionFactory        this.afterPropertiesSet();    }    return this.sqlSessionFactory;}

追踪afterPropertiesSet办法

org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSetorg.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactoryprotected SqlSessionFactory buildSqlSessionFactory() throws Exception {    XMLConfigBuilder xmlConfigBuilder = null;    Configuration targetConfiguration;    // -- configuration已创立    if (this.configuration != null) {        targetConfiguration = this.configuration;    }     // -- configuration未创立,但有配置文件地址:XPath形式解析配置文件    else if (this.configLocation != null) {        xmlConfigBuilder             = new XMLConfigBuilder(this.configLocation.getInputStream(),                      (String)null, this.configurationProperties);        targetConfiguration = xmlConfigBuilder.getConfiguration();    }     // -- 采纳默认配置创立configuration    else {        targetConfiguration = new Configuration();    }        // -------- 略过typeAliases、typeHandler等设置 ---------        targetConfiguration.setEnvironment(            // 初始化Environment        new Environment(this.environment,              (TransactionFactory)(this.transactionFactory == null ?                      // ### 默认采纳SpringManagedTransactionFactory治理事务                     new SpringManagedTransactionFactory() :                          this.transactionFactory),                  this.dataSource));    return this.sqlSessionFactoryBuilder.build(targetConfiguration);}

SqlSessionFactory创立过程,与mybatis独自应用时简直完全一致;惟一的差异在于默认设置SpringManagedTransactionFactory治理事务(mybatis默认采纳jdbc形式治理事务,行将事务管制交给DB)

三、SqlSessionTemplate

接下来看看SqlSession的解决
MybatisAutoConfiguration里没有SqlSession只有SqlSessionTemplate

@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {    ExecutorType executorType = this.properties.getExecutorType();    return executorType != null ?         new SqlSessionTemplate(sqlSessionFactory, executorType)          // 通过构造函数创立         : new SqlSessionTemplate(sqlSessionFactory);}

看看构造函数做了什么。

org.mybatis.spring.SqlSessionTemplate#SqlSessionTemplatepublic SqlSessionTemplate(SqlSessionFactory sqlSessionFactory,         ExecutorType executorType,         PersistenceExceptionTranslator exceptionTranslator) {    this.sqlSessionFactory = sqlSessionFactory;    this.executorType = executorType;    this.exceptionTranslator = exceptionTranslator;    this.sqlSessionProxy =         // 创立sqlsession代理        (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),              new Class[]{SqlSession.class},                // ## invocationHandler               new SqlSessionTemplate.SqlSessionInterceptor());}

间接查看代理的invocationHandler的invoke()

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invokepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    // 1.sqlSession创立,外部实现:session = sessionFactory.openSession(executorType)    SqlSession sqlSession         = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,                          SqlSessionTemplate.this.executorType,                             SqlSessionTemplate.this.exceptionTranslator);    Object unwrapped;    try {        // 2.原办法执行        Object result = method.invoke(sqlSession, args);        unwrapped = result;    } catch (Throwable var11) {        unwrapped = ExceptionUtil.unwrapThrowable(var11);        throw (Throwable)unwrapped;    } finally {        // 3.sqlSession敞开        if (sqlSession != null) {            SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);        }    }    return unwrapped;}

当SqlSessionTemplate中封装的一系列数据库操作方法(如下)被调用时,sqlSession会被主动创立和销毁

public <T> T selectOne(String statement) {    return this.sqlSessionProxy.selectOne(statement);}public int insert(String statement) {    return this.sqlSessionProxy.insert(statement);}public int update(String statement) {    return this.sqlSessionProxy.update(statement);}

Spring应用SqlSessionTemplate代替mybatis的SqlSession,次要为了主动创立和销毁SqlSession。
因为SqlSession是线程不平安的类,不能复用。

四、Mapper创立

SqlSessionTemplate类中提供的一系列办法中,也包含getMapper()办法:

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

这仿佛就和mybatis的应用形式接轨了。但认真一想又感觉不对,在工作中咱们获取mapper并不是通过这种形式。

spring环境下mapper的真正打开方式是这样才对:

@AutowireUserMapper userMapper;

翻译一下就是通过上下文获取,即:context.getMapper(Class clz);

springboot是怎么主动拆卸Mapper的?

答案仍然在MybatisAutoConfiguration中。

@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean

追踪导入的MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar

org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitionspublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,                                 BeanDefinitionRegistry registry) {    // 其实这行日志曾经解释的很分明了    MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");    List<String> packages = AutoConfigurationPackages.get(this.beanFactory);        // ### 扫描器:扫描packages下所有被@Mapper润饰或接口    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);    if (this.resourceLoader != null) {        scanner.setResourceLoader(this.resourceLoader);    }    scanner.setAnnotationClass(Mapper.class);    scanner.registerFilters();    scanner.doScan(StringUtils.toStringArray(packages));}

察看scanner.doScan办法:

org.mybatis.spring.mapper.ClassPathMapperScanner#doScanorg.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScanorg.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinitionorg.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinitionorg.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition// DefaultListableBeanFactory的属性,外面寄存了待初始化的“对象定义”,初始化后的对象都将放入spring上下文中private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);// ————— 以上为属性 ——————// 最终放到了beanDefinitionMapthis.beanDefinitionMap.put(beanName, beanDefinition);
  1. 在SqlSessionFactory创立环节,通过解析mapper.xml,将xml中指定的mapper转换成了代理对象(mybatis逻辑)
  2. 在MybatisAutoConfiguration中,又通过Scaner扫描@Mapper注解润饰的类,实现注入(spring逻辑)

附录

P6-P7常识合辑