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