首先,我们有了Spring,第一个要干掉的就是SqlSessionFactory的维护了,我们要想办法读取”mybatis-config.xml“配置后,将SqlSessionFactory作为一个SpringBean交给SpringIOC管理。
其次,同样的,我们不可能每次都通过sqlSession.getMapper()来获取我们需要的Mapper代理实例,所以第二个要干掉的就是Mapper的维护,我们同样要想办法将所有的Mapper处理成SpringBean交给SpringIOC,这样我们就能够将SpringBean依赖注入到任何地方了。
思考过后,我们来看看Spring是怎么做的吧。当我们需要结合Spring使用MyBatis的时候,第一步便是添加一个mybatis-spring的Jar到项目里来,那么秘密都在这里了。
MyBatis-Spring如下便是MyBatis-Spring这个Jar的项目结构了,应该能看到大家使用的时候熟悉的组件吧。如MapperScan注解。眼尖的伙伴应该能看到几个类:SqlSessionFactoryBean、MapperFactoryBean、MapperScannerConfigurer、SpringManagedTransaction,这几个类将会是接下来探讨的重点。
刚刚我们聊到了,首先需要干掉的就是SqlSessionFactory的低端维护方式。我们先看看SqlSessionFactory这个类的继承树。
可以看到它实现了FactoryBean,这就意味着它一定有一个getObject()方法,用于返回交给Spring管理的实例;
它还实现了InitializingBean,这就意味着在这个Bean的初始化时,Spring会回调它的afterPropertiesSet()方法。(Spring事件本次不讨论) 将SqlSessionFactory交给Spring管理-FactoryBean#getObject()
我们先看看,这个SqlSessionFactoryBean交给Spring管理的对象是怎样构建的。
/** * {@inheritDoc} */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { // 如果sqlSessionFactory为空,则显式调用afterPropertiesSet()方法 afterPropertiesSet(); } return this.sqlSessionFactory; // 返回sqlSessionFactory } @Override public Class<? extends SqlSessionFactory> getObjectType() { return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); }可以看到源码中首先是判空,如果为空则显式调用afterPropertiesSet()方法,最后将sqlSessionFactory返回。那么可以猜出,afterPropertiesSet()方法大概率是构造sqlSessionFactory的了。
SqlSessionFactory的构建首先看看afterPropertiesSet()方法
/** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); this.sqlSessionFactory = buildSqlSessionFactory(); // 核心逻辑在buildSqlSessionFactory()中。 }跟进看看buildSqlSessionFactory()方法,请重点看标了注释的,其他的可以不用太关注细节,主要是一些配置的初始化工作。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; // 初始化Configuration全局配置对象。 if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { // 读取指定的配置文件 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } // 如下将是对MyBatis的基础配置做初始化,如扫描注册别名、注册Plugins、注册TypeHandler、配置缓存、配置数据源等等。 if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } if (this.cache != null) { configuration.addCache(this.cache); } if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception ex) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); } finally { ErrorContext.instance().reset(); } } // 配置事务管理类,将不再由MyBatis管理(之前配置为“JDBC”对应的MyBatis的JdbcTransactionFactory)。 if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); // 扫描Mapper.xml以及对应的Mapper接口 if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } //前置条件准备好后,创建SqlSessionFactory对象 return this.sqlSessionFactoryBuilder.build(configuration); }总体就是读取MyBatis的配置,初始化Configuration全局配置对象,根据整合的配置创建出SqlSessionFactory。