Mybatis与spring集成时,在spring.xml中会配置两个重要的类,SqlSessionFactoryBean与MapperScannerConfigure。用于完成mybatis的加载与配置。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/rsms/iot/mapper/*.xml"/> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/> </bean> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.rsms.iot.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
一、Mybatis核心类图
核心类是SqlSessionFactory和SqlSession
mybatis类图.png
二、Mybatis加载过程
Mybatis自带的加载过程,集成到spring之后,加载过程会有一些变化,主要是bean要提交给spring容器来管理
mybatis加载过程.png
三、SqlSessionFactoryBean介绍
spring配置mybatis的加载入口
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:com/rsms/iot/mapper/*.xml"/> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/> </bean>
configLocation指定mybatis的全局配置位置
mapperLocations指定隐射文件的配置位置
SqlSessionFactoryBean实现接口InitializingBean,在bean加载完成后执行
afterPropertiesSet完成SqlSessionFactory的创建。SqlSessionFactoryBean重新实现了mybatis原生的SqlSessionFactory的构建过程。在buildSqlSessionFactory()方法中通过XMLConfigBuilder完成对全局配置文件的解析,并构造Configuration对象。Configuration对象是全局唯一的提供了mybatis的所有配置项。
然后解析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"); } }
mapper文件是通过XMLMapperBuilder解析
public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); } private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); //过滤出所有的curd sql片段 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }
循环解析所有mapper.xml文件,过滤出curd sql片段,构造MappedStatment(一个sql是一个MappedStatment)存放到Configuration对象的mappedStatements中,mappedStatements是StrictMap,继承hashmap,在存放的时候会存放两份,一份是id+namespace作为key值,一份是id作为key值。value值就是MappedStatment对象。