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

相關文章
相關標籤/搜索