經過實例結合源碼的方式解讀,其中涉及到的文件來自於筆者的Github畢設項目,引用的jar包爲
mybatis-spring-1.3.0.jar
java
Mybatis是基於ORM(Object relation mapping)思想而開發的框架插件,本質原理用一句筆者的話即是使用了JAVA鏈接數據庫的方式來執行相應的SQL(PreparedStatement),並在此基礎上提供了豐富的動態SQL配置以及緩存等概念。(後續文章會說起)mysql
其基本上是企業與傳統數據庫(Oracle/Mysql等)交互經常使用的數據庫持久層框架,以JAVA語言來講,其與Spring的搭配使用也是廣爲流傳。git
本文將從源碼的角度淺析Mybatis與Spring的親密搭配github
筆者本文只關注SqlSessionFactoryBean的配置,樣例以下spring
<!--數據源,引用common-pool包的數據源類--> <bean id="dataSource-mysql" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="initialSize" value="1" /> <property name="maxActive" value="20" /> </bean> <!--sqlSessionFactoryBean建立--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource-mysql" /> <property name="typeAliasesPackage" value="com.du.wx.model" /> <property name="mapperLocations" value="classpath:com/du/wx/resources/mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- joggle表明接口 --> <property name="basePackage" value="com.du.wx.mapper.joggle" /> </bean>
由上述的配置可知,在配置SqlSessionFactory數據庫會話工廠Bean對象時必須依賴相應的數據源屬性dataSourcesql
筆者分層次來解析此類數據庫
優先觀察SqlSessionFactoryBean的內部屬性,代碼片斷以下apache
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class); private Resource configLocation; private Configuration configuration; private Resource[] mapperLocations; private DataSource dataSource; private TransactionFactory transactionFactory; private Properties configurationProperties; private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); private SqlSessionFactory sqlSessionFactory; //EnvironmentAware requires spring 3.1 private String environment = SqlSessionFactoryBean.class.getSimpleName(); private boolean failFast; private Interceptor[] plugins; private TypeHandler<?>[] typeHandlers; private String typeHandlersPackage; private Class<?>[] typeAliases; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; //issue #19. No default provider. private DatabaseIdProvider databaseIdProvider; private Class<? extends VFS> vfs; private Cache cache; private ObjectFactory objectFactory; private ObjectWrapperFactory objectWrapperFactory;
經常使用的屬性以下:編程
configLocation mybatis主配置文件路徑,支持classpath語法緩存
mapperLocations 指定mybatis的mapper配置文件,支持classpath語法
dataSource 數據源
typeAliasesPackage 指定model層類名的別名掃描包,這與mapper配置中的parameterType和resultType搭配使用
緊接着看下其複寫的afterPropertiesSet()方法,代碼以下
@Override public void afterPropertiesSet() throws Exception { //datasource not allowed to be null. 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(); }
與前文說起的對應,必須確保dataSource屬性已經獲得配置。
最後查看關鍵實例方法buildSqlSessionFactory()方法,因爲片斷過長,筆者截取幾段來進行講解
Mybatis主文件加載
Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; 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) { //若是spring配置中configLocation屬性不爲空,則加載指定的Mybatis配置 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { //不然則採用默認的Mybatis配置,@see Configuration if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property `configuration` or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); configuration.setVariables(this.configurationProperties); }
經過上述配置代表,內部屬性configLocation是非必須配置項
model別名映射掃描
if (hasLength(this.typeAliasesPackage)) { //截取typeAliasesPackage屬性的包,支持,或; String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { //對應方法的目的是經過掃描包獲得其包以及子包目錄下的全部Class,而後爲每一個class註冊別名 configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } }
即對內部屬性typeAliasesPackage支持,;
做爲分隔符以加載多個目錄,主要是爲了方便編程人員使用parameterType/resultType等屬性
mapper配置文件加載
if (!isEmpty(this.mapperLocations)) { //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"); } }
關鍵處理XML配置的mapper文件,用到了mybatis包中的org.apache.ibatis.builder.xml.XMLMapperBuilder類進行解析。這個後續分析
Configuration-全部關於Mybatis的信息均存儲在Mybatis包中的org.apache.ibatis.session.Configuration類上
return this.sqlSessionFactoryBuilder.build(configuration);
Spring呢,就經過SqlSessionFactoryBuilder類來建立相應的SqlSessionFactory對象。具體的因爲篇幅過長,就放在後文分析。
從上文即可以看出來Spring框架是如何整合Mybatis的,就是這個Mybatis的關鍵類Configuration。而相應的開關數據庫等一系列的功能都是經過Spring的SqlSessionFactory對象來操控的。這也是二者結合最核心的地方