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的執行的完整流程。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 決定接管 sqlSessionFactory 和 sqlSession,而且爲 sqlSession 建立代理類 sqlSessionProxy。除此以外,mybatis-spring 決定掃描全部interface層的mapper,而後接管全部 mapper 的代理類。2條線咱們分開來看。緩存
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,此處再也不展開。
interface首先要被掃描,而後挨個生成代理類,等待調用。下面咱們來看看這個過程:
說明:對於第4點,MapperFactoryBean
是一個工廠bean,在spring容器裏,工廠bean是有特殊用途的,當spring將工廠bean注入到其餘bean裏時,它不是注入工廠bean自己,而是調用bean的getObject方法。
sqlSessionFactory的初始化完成後,mapper的掃描和代理類被建立出來後,有了這兩個前提條件,咱們就能夠來最終捋一捋 mybatis-spring 的調用過程了。
業務代碼中,須要查詢數據庫,因而調用 mapper 中的一個方法
以 SELECT 狀況舉例,將會執行 sqlSession.selectList。此時取出的,就是mapper的一個代理類
sqlSessionTemplate.sqlSessionProxy.selectList
咱們能夠發現,在 mybatis-spring 框架中,真正 sqlSession 的建立,是在調用interface中的方法的時候才進行的。更細節的過程能夠參考上文。
創做時間:06/08/2019 21:00