mybatis-spring 启动过程和调用过程

mybatis-spring 能够为咱们作什么

mybatis框架已经很不错了,它把配置和执行sql的通用过程抽象出来。只要你符合mybatis框架的要求,首先有正确的配置,而后有model,interface层,sql语句,还有bean定义让interface和sql关联起来,那么当你执行interface中的方法的时候,mybatis框架就会为你找到对应的sql的可执行的statement,而后执行并返回结果。spring

但是这样还不够,最大的问题在于许多bean定义,必须一个一个手写,而且要保证interface和sql的名称和位置要填写正确。mybatis-spring最大的贡献在于它的bean扫描机制,只要注解使用正确,那么它能够为你自动扫描全部interface和sql语句,而且创建bean定义让它们关联起来。Spring还能够为动态的Bean定义建立缓存,这很是酷。sql

另外,既然融入了Spring框架,那么mybatis配置信息和SqlSessionFactory之类的信息,也能够用 Spring Bean 来管理。Spring 能够在它的层面上为它们作一些缓存。数据库

mybatis执行sql的完整流程

咱们回顾一下在单一的mybatis的机制中,配置的加载和sql的执行的完整流程。segmentfault

// 解析配置文件,生成配置
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

// 根据配置,构建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  

// 获得一个真正可用的SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

// 从SqlSession获取interface的代理
ArticleMapper articleMapperProxy = sqlSession.getMapper(ArticleMapper.class);

// 执行代理类中的方法
Article article = articleMapperProxy.selectByPrimaryKey("123");

// 如下省略对 article 的操做

 

mybatis-spring 启动过程

咱们先来讲说mybatis-spring框架的启动过程。mybatis-spring 决定接管 sqlSessionFactory 和 sqlSession,而且为 sqlSession 建立代理类 sqlSessionProxy。除此以外,mybatis-spring 决定扫描全部interface层的mapper,而后接管全部 mapper 的代理类。2条线咱们分开来看。缓存

线1:sqlSessionFactory 和 sqlSession 的初始化

  1. 建立 sqlSessionFactoryBean,这是一个被Spring管理的工厂bean
  2. 建立 sqlSessionFactory,这是一个 Spring 管理的 Bean,属于 mybatis 范畴
  3. 建立 sqlSessionTemplate,其中使用到了 sqlSessionFactory,属于 mybatis-spring 范畴
  4. 建立 sqlSessionProxy

SqlSessionFactory是一个十分重要的工厂类,让咱们来回顾一下SqlSessionFactory中有哪些信息:mybatis

# sqlSessionFactory 中的重要信息

sqlSessionFactory
    configuration
        environment        # 里面有 dataSource 信息
        mapperRegistry
            config         # 里面有配置信息
            knownMappers   # 里面有全部的 mapper
        mappedStatements   # 里面有全部 mapper 的全部方法
        resultMaps         # 里面有全部 xml 中的全部 resultMap
        sqlFragments       # 里面有全部的 sql 片断

这些信息很是重要,在不久的未来建立 sqlSessionProxy 和 未来建立 mapperProxy 的时候,都须要使用里面的信息。app

在 mybatis-spring 框架中,sqlSessionFactory由Spring管理,让咱们来看一下 sqlSessionFactory 是如何建立出来的。框架

 1 @Bean(name = "sqlSessionFactory")
 2 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
 3     SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
 4     factory.setDataSource(dataSource);
 5     if (StringUtils.hasText(this.properties.getConfig())) {
 6         factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfig()));
 7     } else {
 8         if (this.interceptors != null && this.interceptors.length > 0) {
 9             factory.setPlugins(this.interceptors);
10         }
11         factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
12         factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
13         factory.setMapperLocations(this.properties.getMapperLocations());
14     }
15     return factory.getObject();
16 }

注意:SqlSessionFactoryBean 是一个工厂bean,它的做用就是解析mybatis 配置(数据源、别名等),而后经过 getObject方法返回一个 SqlSessionFactory 实例。咱们先看下SqlSessionFactoryBean是在初始化的时候做了哪些工做。ide

让咱们来看一下 SqlSessionFactoryBean 的源码:ui

 1 public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
 2     private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
 3     private Resource configLocation;
 4     private Configuration configuration;
 5     private Resource[] mapperLocations;
 6     private DataSource dataSource;
 7     private TransactionFactory transactionFactory;
 8     private Properties configurationProperties;
 9     private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
10     private SqlSessionFactory sqlSessionFactory;
11     private String environment = SqlSessionFactoryBean.class.getSimpleName();
12     private boolean failFast;
13     private Interceptor[] plugins;
14     private TypeHandler<?>[] typeHandlers;
15     private String typeHandlersPackage;
16     private Class<?>[] typeAliases;
17     private String typeAliasesPackage;
18     private Class<?> typeAliasesSuperType;
19     private DatabaseIdProvider databaseIdProvider;
20     private Class<? extends VFS> vfs;
21     private Cache cache;
22     private ObjectFactory objectFactory;
23     private ObjectWrapperFactory objectWrapperFactory;
24 
25     public SqlSessionFactoryBean() {
26     }
27     ...
28 }

咱们能够看到,这个类实现了FactoryBean、InitializingBean和ApplicationListener接口,对应的接口在bean初始化的时候又执行了一些特定的方法,此处再也不展开。如今来看看都有哪些重要的方法会被执行,这些方法又作了哪些工做。

// FactoryBean中的方法
public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        this.afterPropertiesSet();
    }

    return this.sqlSessionFactory;
}

经过观察代码,getObject方法最终返回 sqlSessionFactory,若是 sqlSessionFactory 为空就会执行 afterPropertiesSet 中的 buildSqlSessionFactory 构建sqlSessionFactory,在构建sqlSessionFactory时mybatis会去解析配置文件,构建configuation。后面的onApplicationEvent主要是监听应用事件时作的一些事情(不展开,有兴趣的同窗能够本身去了解下)。

咱们来看看 afterPropertiesSet 方法是怎么将属性设置进去的:

// InitializingBean中的方法
public void afterPropertiesSet() throws Exception {
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
    
    // 看到了咱们熟悉的build方法
    this.sqlSessionFactory = this.buildSqlSessionFactory();
}

buildSqlSessionFactory 的主要方法是:this.sqlSessionFactoryBuilder.build(configuration); 此处再也不展开。到这里为止,sqlSessionFactory 已经建立完成,下面咱们来简单看看 sqlSessionTemplate 的建立过程:

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory, this.properties.getExecutorType());
}

若是跟踪进去,就会发现 new SqlSessionTemplate 的同时,会建立 sqlSessionProxy,此处再也不展开。

 

线2:mapper 的扫描和代理类的建立

interface首先要被扫描,而后挨个生成代理类,等待调用。下面咱们来看看这个过程:

  1. 使用 MapperScannerConfigurer
  2. scan() 扫描mapper
  3. setBeanClass
  4. 获得 MapperFactoryBean
  5. MapperFactoryBean.getObject() 方法
  6. configuration.getMapper
  7. getMapper
  8. MapperRegistry
  9. knownMappers
  10. 最终获得一组 MapperProxy,他们是原始 mapper 的代理。MapperProxy 实现了 InvocationHandler 接口,其中有 invoke 方法,在实际调用的时候执行。

说明:对于第4点,MapperFactoryBean 是一个工厂bean,在spring容器里,工厂bean是有特殊用途的,当spring将工厂bean注入到其余bean里时,它不是注入工厂bean自己,而是调用bean的getObject方法。

 

mybatis-spring 调用过程

sqlSessionFactory的初始化完成后,mapper的扫描和代理类被建立出来后,有了这两个前提条件,咱们就能够来最终捋一捋 mybatis-spring 的调用过程了。

  1. 业务代码中,须要查询数据库,因而调用 mapper 中的一个方法

  2. MapperProxy invoke
    • 2.1 if 判断
    • 2.2 else if 判断
    • 2.3 cachedMapperMethod 重点方法
    • 2.4 mapperMethod.execute 重点方法,实际执行的方法
  3. 根据sql语句的类型,分状况处理
    • case INSERT
    • case UPDATE
    • case DELETE
    • case SELECT
    • case FLUSH
  4. 以 SELECT 状况举例,将会执行 sqlSession.selectList。此时取出的,就是mapper的一个代理类

  5. sqlSessionTemplate.sqlSessionProxy.selectList

  6. SqlSessionInterceptor invoke
    • 6.1 getSqlSession,用到了 sqlSessionFactory,使用了sqlSessionHolder 技术,有就拿一个,没有就新建一个。不管如何,都会有一个 sqlSession
    • 6.2 defaultSqlSession.selectList 重点,以后是 query -> queryFromDatabase -> doQuery -> 1. prepareStatement 2. execute
    • 6.3 closeSqlSession

咱们能够发现,在 mybatis-spring 框架中,真正 sqlSession 的建立,是在调用interface中的方法的时候才进行的。更细节的过程能够参考上文。

 

参考资料

  • https://segmentfault.com/a/1190000015165470

 

创做时间:06/08/2019 21:00

相关文章
相关标签/搜索