Mybatis 源码分析一、 SqlSessionFactory的创建过程

本次解析是基于Mybatis与Spring整合的方式,分析Mybatis的初始化过程。
一、配置文件解析过程
    Spring与Mybatis整合配置过程
  1.1 配置数据源
     
     
     
     
  1. xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
  6. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
  8. <bean name="tcoin-dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  9. <property name="url" value="${tcoin.jdbc.url}"/>
  10. <property name="username" value="${tcoin.jdbc.user}"/>
  11. <property name="password" value="${tcoin.jdbc.password}"/>
  12. <property name="initialSize" value="${tcoin.jdbc.initialSize}"/>
  13. <property name="maxActive" value="${tcoin.jdbc.maxActive}"/>
  14. <property name="minIdle" value="${tcoin.jdbc.minIdle}"/>
  15. <property name="maxWait" value="${jdbc.maxWait}"/>
  16. <property name="poolPreparedStatements" value="${jdbc.poolPreparedStatements}"/>
  17. <property name="maxPoolPreparedStatementPerConnectionSize" value="${jdbc.maxPoolPreparedStatementPerConnectionSize}"/>
  18. <property name="validationQuery" value="${jdbc.validationQuery}"/>
  19. <property name="testOnBorrow" value="${jdbc.testOnBorrow}"/>
  20. <property name="testOnReturn" value="${jdbc.testOnReturn}"/>
  21. <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
  22. <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
  23. <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
  24. <property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>
  25. <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
  26. <property name="logAbandoned" value="${jdbc.logAbandoned}"/>
  27. <property name="filters" value="${jdbc.filters}"/>
  28. bean>
  29. <bean id="tcoin-sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  30. <property name="configLocation" value="classpath:config/framework-mybatis.xml" />
  31. <property name="dataSource" ref="tcoin-dataSource" />
  32. <property name="plugins">
  33. <list>
  34. <ref bean="offsetLimitIntercepter" />
  35. list>
  36. property>
  37. <property name="mapperLocations"
  38. value="classpath*:com/tairanchina/md/tcoin/rule/dal/dao/*Mapper*.xml" />
  39. bean>
  40. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  41. <property name="annotationClass" value="javax.annotation.Resource" />
  42. <property name="basePackage" value="com.tairanchina.md.tcoin.rule.dal" />
  43. <property name="sqlSessionFactoryBeanName" value="tcoin-sqlSessionFactory" />
  44. bean>
  45. beans>
 1.2 Mybaits配置文件
      
      
      
      
  1. xml version="1.0" encoding="UTF-8" ?>
  2. DOCTYPE configuration[
  3. ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
  4. ELEMENT databaseIdProvider (property*)>
  5. ATTLIST databaseIdProvider
  6. type CDATA #REQUIRED
  7. >
  8. ELEMENT properties (property*)>
  9. ATTLIST properties
  10. resource CDATA #IMPLIED
  11. url CDATA #IMPLIED
  12. >
  13. ELEMENT property EMPTY>
  14. ATTLIST property
  15. name CDATA #REQUIRED
  16. value CDATA #REQUIRED
  17. >
  18. ELEMENT settings (setting+)>
  19. ELEMENT setting EMPTY>
  20. ATTLIST setting
  21. name CDATA #REQUIRED
  22. value CDATA #REQUIRED
  23. >
  24. ELEMENT typeAliases (typeAlias*,package*)>
  25. ELEMENT typeAlias EMPTY>
  26. ATTLIST typeAlias
  27. type CDATA #REQUIRED
  28. alias CDATA #IMPLIED
  29. >
  30. ELEMENT typeHandlers (typeHandler*,package*)>
  31. ELEMENT typeHandler EMPTY>
  32. ATTLIST typeHandler
  33. javaType CDATA #IMPLIED
  34. jdbcType CDATA #IMPLIED
  35. handler CDATA #REQUIRED
  36. >
  37. ELEMENT objectFactory (property*)>
  38. ATTLIST objectFactory
  39. type CDATA #REQUIRED
  40. >
  41. ELEMENT objectWrapperFactory (property*)>
  42. ATTLIST objectWrapperFactory
  43. type CDATA #REQUIRED
  44. >
  45. ELEMENT plugins (plugin+)>
  46. ELEMENT plugin (property*)>
  47. ATTLIST plugin
  48. interceptor CDATA #REQUIRED
  49. >
  50. ELEMENT environments (environment+)>
  51. ATTLIST environments
  52. default CDATA #REQUIRED
  53. >
  54. ELEMENT environment (transactionManager,dataSource)>
  55. ATTLIST environment
  56. id CDATA #REQUIRED
  57. >
  58. ELEMENT transactionManager (property*)>
  59. ATTLIST transactionManager
  60. type CDATA #REQUIRED
  61. >
  62. ELEMENT dataSource (property*)>
  63. ATTLIST dataSource
  64. type CDATA #REQUIRED
  65. >
  66. ELEMENT mappers (mapper*,package*)>
  67. ELEMENT mapper EMPTY>
  68. ATTLIST mapper
  69. resource CDATA #IMPLIED
  70. url CDATA #IMPLIED
  71. class CDATA #IMPLIED
  72. >
  73. ELEMENT package EMPTY>
  74. ATTLIST package
  75. name CDATA #REQUIRED
  76. >
  77. ]>
  78. <configuration>
  79. <settings>
  80. <setting name="mapUnderscoreToCamelCase" value="true" />
  81. <setting name="jdbcTypeForNull" value="NULL" />
  82. <setting name="cacheEnabled" value="false" />
  83. settings>
  84. configuration>
1.3 配置文件解析过程
    本介绍略过Spring解析配置文件的过程,只分析Mybatis解析配置文件,初始化Myatis配置的过程。
1.3.1 SqlSessionFactoryBean创建过程
    SqlSessionFactory是Mybatis的核心类之一,主要功能是提供创建Mybatis的核心接口SqlSession。
    SqlSessionFactory的创建是使用构造者模式进行创建,SqlSessionFactoryBean源码位于org.mybaits.spring包下。源码如下:
      
      
      
      
  1. package org.mybatis.spring;
  2. import static org.springframework.util.Assert.notNull;
  3. import static org.springframework.util.ObjectUtils.isEmpty;
  4. import static org.springframework.util.StringUtils.hasLength;
  5. import static org.springframework.util.StringUtils.tokenizeToStringArray;
  6. import java.io.IOException;
  7. import java.sql.SQLException;
  8. import java.util.Properties;
  9. import javax.sql.DataSource;
  10. import org.apache.ibatis.builder.xml.XMLConfigBuilder;
  11. import org.apache.ibatis.builder.xml.XMLMapperBuilder;
  12. import org.apache.ibatis.executor.ErrorContext;
  13. import org.apache.ibatis.logging.Log;
  14. import org.apache.ibatis.logging.LogFactory;
  15. import org.apache.ibatis.mapping.DatabaseIdProvider;
  16. import org.apache.ibatis.mapping.Environment;
  17. import org.apache.ibatis.plugin.Interceptor;
  18. import org.apache.ibatis.reflection.factory.ObjectFactory;
  19. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
  20. import org.apache.ibatis.session.Configuration;
  21. import org.apache.ibatis.session.SqlSessionFactory;
  22. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  23. import org.apache.ibatis.transaction.TransactionFactory;
  24. import org.apache.ibatis.type.TypeHandler;
  25. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
  26. import org.springframework.beans.factory.FactoryBean;
  27. import org.springframework.beans.factory.InitializingBean;
  28. import org.springframework.context.ApplicationEvent;
  29. import org.springframework.context.ApplicationListener;
  30. import org.springframework.context.ConfigurableApplicationContext;
  31. import org.springframework.context.event.ContextRefreshedEvent;
  32. import org.springframework.core.NestedIOException;
  33. import org.springframework.core.io.Resource;
  34. import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
  35. public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
  36. private static final Log logger = LogFactory.getLog(SqlSessionFactoryBean.class);
  37. private Resource configLocation;
  38. private Resource[] mapperLocations;
  39. private DataSource dataSource;
  40. private TransactionFactory transactionFactory;
  41. private Properties configurationProperties;
  42. private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  43. private SqlSessionFactory sqlSessionFactory;
  44. private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1
  45. private boolean failFast;
  46. private Interceptor[] plugins;
  47. private TypeHandler[] typeHandlers;
  48. private String typeHandlersPackage;
  49. private Class[] typeAliases;
  50. private String typeAliasesPackage;
  51. private Class typeAliasesSuperType;
  52. private DatabaseIdProvider databaseIdProvider; // issue #19. No default provider.
  53. private ObjectFactory objectFactory;
  54. private ObjectWrapperFactory objectWrapperFactory;
  55. public void setObjectFactory(ObjectFactory objectFactory) {
  56. this.objectFactory = objectFactory;
  57. }
  58. public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
  59. this.objectWrapperFactory = objectWrapperFactory;
  60. }
  61. public DatabaseIdProvider getDatabaseIdProvider() {
  62. return databaseIdProvider;
  63. }
  64. public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
  65. this.databaseIdProvider = databaseIdProvider;
  66. }
  67. public void setPlugins(Interceptor[] plugins) {
  68. this.plugins = plugins;
  69. }
  70. public void setTypeAliasesPackage(String typeAliasesPackage) {
  71. this.typeAliasesPackage = typeAliasesPackage;
  72. }
  73. public void setTypeAliasesSuperType(Class typeAliasesSuperType) {
  74. this.typeAliasesSuperType = typeAliasesSuperType;
  75. }
  76. public void setTypeHandlersPackage(String typeHandlersPackage) {
  77. this.typeHandlersPackage = typeHandlersPackage;
  78. }
  79. public void setTypeHandlers(TypeHandler[] typeHandlers) {
  80. this.typeHandlers = typeHandlers;
  81. }
  82. public void setTypeAliases(Class[] typeAliases) {
  83. this.typeAliases = typeAliases;
  84. }
  85. /**
  86. public void setFailFast(boolean failFast) {
  87. this.failFast = failFast;
  88. }
  89. public void setConfigLocation(Resource configLocation) {
  90. this.configLocation = configLocation;
  91. }
  92. public void setMapperLocations(Resource[] mapperLocations) {
  93. this.mapperLocations = mapperLocations;
  94. }
  95. public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
  96. this.configurationProperties = sqlSessionFactoryProperties;
  97. }
  98. public void setDataSource(DataSource dataSource) {
  99. if (dataSource instanceof TransactionAwareDataSourceProxy) {
  100. this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
  101. } else {
  102. this.dataSource = dataSource;
  103. }
  104. }
  105. public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
  106. this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
  107. }
  108. /**
  109. public void setTransactionFactory(TransactionFactory transactionFactory) {
  110. this.transactionFactory = transactionFactory;
  111. }
  112. public void setEnvironment(String environment) {
  113. this.environment = environment;
  114. }
  115. public void afterPropertiesSet() throws Exception {
  116. notNull(dataSource, "Property 'dataSource' is required");
  117. notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  118. this.sqlSessionFactory = buildSqlSessionFactory();
  119. }
  120. protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
  121. Configuration configuration;
  122. XMLConfigBuilder xmlConfigBuilder = null;
  123. if (this.configLocation != null) {
  124. xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  125. configuration = xmlConfigBuilder.getConfiguration();
  126. } else {
  127. if (logger.isDebugEnabled()) {
  128. logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
  129. }
  130. configuration = new Configuration();
  131. configuration.setVariables(this.configurationProperties);
  132. }
  133. if (this.objectFactory != null) {
  134. configuration.setObjectFactory(this.objectFactory);
  135. }
  136. if (this.objectWrapperFactory != null) {
  137. configuration.setObjectWrapperFactory(this.objectWrapperFactory);
  138. }
  139. if (hasLength(this.typeAliasesPackage)) {
  140. String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
  141. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  142. for (String packageToScan : typeAliasPackageArray) {
  143. configuration.getTypeAliasRegistry().registerAliases(packageToScan,
  144. typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
  145. if (logger.isDebugEnabled()) {
  146. logger.debug("Scanned package: '" + packageToScan + "' for aliases");
  147. }
  148. }
  149. }
  150. if (!isEmpty(this.typeAliases)) {
  151. for (Class typeAlias : this.typeAliases) {
  152. configuration.getTypeAliasRegistry().registerAlias(typeAlias);
  153. if (logger.isDebugEnabled()) {
  154. logger.debug("Registered type alias: '" + typeAlias + "'");
  155. }
  156. }
  157. }
  158. if (!isEmpty(this.plugins)) {
  159. for (Interceptor plugin : this.plugins) {
  160. configuration.addInterceptor(plugin);
  161. if (logger.isDebugEnabled()) {
  162. logger.debug("Registered plugin: '" + plugin + "'");
  163. }
  164. }
  165. }
  166. if (hasLength(this.typeHandlersPackage)) {
  167. String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
  168. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  169. for (String packageToScan : typeHandlersPackageArray) {
  170. configuration.getTypeHandlerRegistry().register(packageToScan);
  171. if (logger.isDebugEnabled()) {
  172. logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
  173. }
  174. }
  175. }
  176. if (!isEmpty(this.typeHandlers)) {
  177. for (TypeHandler typeHandler : this.typeHandlers) {
  178. configuration.getTypeHandlerRegistry().register(typeHandler);
  179. if (logger.isDebugEnabled()) {
  180. logger.debug("Registered type handler: '" + typeHandler + "'");
  181. }
  182. }
  183. }
  184. if (xmlConfigBuilder != null) {
  185. try {
  186. xmlConfigBuilder.parse();
  187. if (logger.isDebugEnabled()) {
  188. logger.debug("Parsed configuration file: '" + this.configLocation + "'");
  189. }
  190. } catch (Exception ex) {
  191. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  192. } finally {
  193. ErrorContext.instance().reset();
  194. }
  195. }
  196. if (this.transactionFactory == null) {
  197. this.transactionFactory = new SpringManagedTransactionFactory();
  198. }
  199. Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
  200. configuration.setEnvironment(environment);
  201. if (this.databaseIdProvider != null) {
  202. try {
  203. configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
  204. } catch (SQLException e) {
  205. throw new NestedIOException("Failed getting a databaseId", e);
  206. }
  207. }
  208. if (!isEmpty(this.mapperLocations)) {
  209. for (Resource mapperLocation : this.mapperLocations) {
  210. if (mapperLocation == null) {
  211. continue;
  212. }
  213. try {
  214. XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  215. configuration, mapperLocation.toString(), configuration.getSqlFragments());
  216. xmlMapperBuilder.parse();
  217. } catch (Exception e) {
  218. throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
  219. } finally {
  220. ErrorContext.instance().reset();
  221. }
  222. if (logger.isDebugEnabled()) {
  223. logger.debug("Parsed mapper file: '" + mapperLocation + "'");
  224. }
  225. }
  226. } else {
  227. if (logger.isDebugEnabled()) {
  228. logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
  229. }
  230. }
  231. return this.sqlSessionFactoryBuilder.build(configuration);
  232. }
  233. public SqlSessionFactory getObject() throws Exception {
  234. if (this.sqlSessionFactory == null) {
  235. afterPropertiesSet();
  236. }
  237. return this.sqlSessionFactory;
  238. }
  239. public Class extends SqlSessionFactory> getObjectType() {
  240. return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  241. }
  242. public boolean isSingleton() {
  243. return true;
  244. }
  245. public void onApplicationEvent(ApplicationEvent event) {
  246. if (failFast && event instanceof ContextRefreshedEvent) {
  247. // fail-fast -> check all statements are completed
  248. this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
  249. }
  250. }
  251. }
我们将这段源码的解析分为两个部分,一部分是Spring对SqlSessionFactroyBean的初始化过程,另一过程是Mybaits的解析配置参数的过程。
    
       首先SqlSessionFactoryBean 实现了了Spring 的FacatoryBean,顾名思义就是Spring的对象创建工厂,Spring在创建对象的时候会调用对象工厂的getObject()方法。
        Spring首先会根据我们我们节点的配置,创建简单的SqlSessionFactoryBean,并对其参数进行初始化,初始化涉及到的参数主要有:
      
      
      
      
  1. private Resource configLocation;
  2. private Resource[] mapperLocations;
  3. private DataSource dataSource;
  4. private TransactionFactory transactionFactory;
  5. private Properties configurationProperties;
  6. private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  7. private SqlSessionFactory sqlSessionFactory;
  8. private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1
  9. private boolean failFast;
  10. private Interceptor[] plugins;
  11. private TypeHandler[] typeHandlers;
  12. private String typeHandlersPackage;
  13. private Class[] typeAliases;
  14. private String typeAliasesPackage;
  15. private Class typeAliasesSuperType;
  16. private DatabaseIdProvider databaseIdProvider; // issue #19. No default provider.
  17. private ObjectFactory objectFactory;
  18. private ObjectWrapperFactory objectWrapperFactory;
        以上参数正对应我们在节点中通过配置的参数。在本例中对应的就是:
        
      
      
      
      
  1. <bean id="tcoin-sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. <property name="configLocation" value="classpath:config/txframework-mybatis.xml" />
  3. <property name="dataSource" ref="tcoin-dataSource" />
  4. <property name="plugins">
  5. <list>
  6. <ref bean="offsetLimitIntercepter" />
  7. list>
  8. property>
  9. <property name="mapperLocations"
  10. value="classpath*:com/tairanchina/md/tcoin/rule/dal/dao/*Mapper*.xml" />
  11. bean>
       本例中Spring初始化创建SqlSessionFactoryBean就会根据我们的配置对其configaLocation、mapperLocations、dataSource、plugins进行初始化。之后会调用其中的getObject()方法。
    
       
       
       
       
  1. public SqlSessionFactory getObject() throws Exception {
  2. if (this.sqlSessionFactory == null) {
  3. afterPropertiesSet();
  4. }
  5. return this.sqlSessionFactory;
  6. }
        对于getObject()方法,由于此时this.sqlSessionFactoty并没有赋值,故接着会执行 afterPropertiesSet()方法。
   
        
        
        
        
  1. public void afterPropertiesSet() throws Exception {
  2. notNull(dataSource, "Property 'dataSource' is required");
  3. notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  4. this.sqlSessionFactory = buildSqlSessionFactory();
  5. }
这时通过两个断言,判断dataSource以及sqlSessionFactoryBuilder(SqlSessionFactory 创建者模式的具体创建SqlSessionFactory创建对象)不能为null。
接着就会执行创建SqlSessionFactory的具体方法。
   
        
        
        
        
        
  1. protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
  2. Configuration configuration;
  3. XMLConfigBuilder xmlConfigBuilder = null;
  4. if (this.configLocation != null) {
  5. xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  6. configuration = xmlConfigBuilder.getConfiguration();
  7. } else {
  8. if (logger.isDebugEnabled()) {
  9. logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
  10. }
  11. configuration = new Configuration();
  12. configuration.setVariables(this.configurationProperties);
  13. }
  14. if (this.objectFactory != null) {
  15. configuration.setObjectFactory(this.objectFactory);
  16. }
  17. if (this.objectWrapperFactory != null) {
  18. configuration.setObjectWrapperFactory(this.objectWrapperFactory);
  19. }
  20. if (hasLength(this.typeAliasesPackage)) {
  21. String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
  22. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  23. for (String packageToScan : typeAliasPackageArray) {
  24. configuration.getTypeAliasRegistry().registerAliases(packageToScan,
  25. typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
  26. if (logger.isDebugEnabled()) {
  27. logger.debug("Scanned package: '" + packageToScan + "' for aliases");
  28. }
  29. }
  30. }
  31. if (!isEmpty(this.typeAliases)) {
  32. for (Class typeAlias : this.typeAliases) {
  33. configuration.getTypeAliasRegistry().registerAlias(typeAlias);
  34. if (logger.isDebugEnabled()) {
  35. logger.debug("Registered type alias: '" + typeAlias + "'");
  36. }
  37. }
  38. }
  39. if (!isEmpty(this.plugins)) {
  40. for (Interceptor plugin : this.plugins) {
  41. configuration.addInterceptor(plugin);
  42. if (logger.isDebugEnabled()) {
  43. logger.debug("Registered plugin: '" + plugin + "'");
  44. }
  45. }
  46. }
  47. if (hasLength(this.typeHandlersPackage)) {
  48. String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
  49. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  50. for (String packageToScan : typeHandlersPackageArray) {
  51. configuration.getTypeHandlerRegistry().register(packageToScan);
  52. if (logger.isDebugEnabled()) {
  53. logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
  54. }
  55. }
  56. }
  57. if (!isEmpty(this.typeHandlers)) {
  58. for (TypeHandler typeHandler : this.typeHandlers) {
  59. configuration.getTypeHandlerRegistry().register(typeHandler);
  60. if (logger.isDebugEnabled()) {
  61. logger.debug("Registered type handler: '" + typeHandler + "'");
  62. }
  63. }
  64. }
  65. if (xmlConfigBuilder != null) {
  66. try {
  67. xmlConfigBuilder.parse();
  68. if (logger.isDebugEnabled()) {
  69. logger.debug("Parsed configuration file: '" + this.configLocation + "'");
  70. }
  71. } catch (Exception ex) {
  72. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  73. } finally {
  74. ErrorContext.instance().reset();
  75. }
  76. }
  77. if (this.transactionFactory == null) {
  78. this.transactionFactory = new SpringManagedTransactionFactory();
  79. }
  80. Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
  81. configuration.setEnvironment(environment);
  82. if (this.databaseIdProvider != null) {
  83. try {
  84. configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
  85. } catch (SQLException e) {
  86. throw new NestedIOException("Failed getting a databaseId", e);
  87. }
  88. }
  89. if (!isEmpty(this.mapperLocations)) {
  90. for (Resource mapperLocation : this.mapperLocations) {
  91. if (mapperLocation == null) {
  92. continue;
  93. }
  94. try {
  95. XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  96. configuration, mapperLocation.toString(), configuration.getSqlFragments());
  97. xmlMapperBuilder.parse();
  98. } catch (Exception e) {
  99. throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
  100. } finally {
  101. ErrorContext.instance().reset();
  102. }
  103. if (logger.isDebugEnabled()) {
  104. logger.debug("Parsed mapper file: '" + mapperLocation + "'");
  105. }
  106. }
  107. } else {
  108. if (logger.isDebugEnabled()) {
  109. logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
  110. }
  111. }
  112. return this.sqlSessionFactoryBuilder.build(configuration);
  113. }
        上述方法简单来说主要完成的工作是根据我们的配置文件构建Conffiguration对象,再由这个对象根据创建者模式的方式创建SqlSessionFactory对象。下面我们一步步解析整个方法的执行过程。
        XMLConfigBuilder 是用于解析配置的XML文件,读取配置参数,并将读取的数据存入Configuration的了类中,几乎我们使用到的Mybatis的所有配置都在这里。
        第一步:
                
         
         
         
         
  1. xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  2. configuration = xmlConfigBuilder.getConfiguration();
            根据我们配置的configuration参数,读取Mybatis的主配置文件,生成Configuration对象,注意此时并没有去真正解析该配置文件。
            同样我们可以看到,我们可以不配置configuration属性,这时Mybatis会给我们执行一个默认的属性。
     第二步:
            根据我们在SqlSessionFactoryBean节点中配置的属性,对Configuration中对应的属性进行赋值,具体每个属性的处理方式,我们会在Configuration解析Mybatis配置文件时,再进一步研究。
            
         
         
         
         
  1. if (this.objectFactory != null) {
  2. configuration.setObjectFactory(this.objectFactory);
  3. }
  4. if (this.objectWrapperFactory != null) {
  5. configuration.setObjectWrapperFactory(this.objectWrapperFactory);
  6. }
  7. if (hasLength(this.typeAliasesPackage)) {
  8. String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
  9. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  10. for (String packageToScan : typeAliasPackageArray) {
  11. configuration.getTypeAliasRegistry().registerAliases(packageToScan,
  12. typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
  13. if (logger.isDebugEnabled()) {
  14. logger.debug("Scanned package: '" + packageToScan + "' for aliases");
  15. }
  16. }
  17. }
  18. if (!isEmpty(this.typeAliases)) {
  19. for (Class typeAlias : this.typeAliases) {
  20. configuration.getTypeAliasRegistry().registerAlias(typeAlias);
  21. if (logger.isDebugEnabled()) {
  22. logger.debug("Registered type alias: '" + typeAlias + "'");
  23. }
  24. }
  25. }
  26. if (!isEmpty(this.plugins)) {
  27. for (Interceptor plugin : this.plugins) {
  28. configuration.addInterceptor(plugin);
  29. if (logger.isDebugEnabled()) {
  30. logger.debug("Registered plugin: '" + plugin + "'");
  31. }
  32. }
  33. }
  34. if (hasLength(this.typeHandlersPackage)) {
  35. String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
  36. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  37. for (String packageToScan : typeHandlersPackageArray) {
  38. configuration.getTypeHandlerRegistry().register(packageToScan);
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
  41. }
  42. }
  43. }
  44. if (!isEmpty(this.typeHandlers)) {
  45. for (TypeHandler typeHandler : this.typeHandlers) {
  46. configuration.getTypeHandlerRegistry().register(typeHandler);
  47. if (logger.isDebugEnabled()) {
  48. logger.debug("Registered type handler: '" + typeHandler + "'");
  49. }
  50. }
  51. }
    
        以上两步操作,完成了我们在Spring bean节点里面配置的参数的解析,下面我们研究Mybatis配置文件的解析过程。
        
        第三步:
         
         
         
         
  1. if (xmlConfigBuilder != null) {
  2. try {
  3. xmlConfigBuilder.parse();
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Parsed configuration file: '" + this.configLocation + "'");
  6. }
  7. } catch (Exception ex) {
  8. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  9. } finally {
  10. ErrorContext.instance().reset();
  11. }
  12. }
        以上代码关键在于xmlConfigBuilder.parse(),也即具体解析Mybatis的配置文件的过程。对应的是XMLConfiguBuilder中的parse()方法。
        
          
          
          
          
  1. public Configuration parse() {
  2. if (parsed) {
  3. throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  4. }
  5. parsed = true;
  6. parseConfiguration(parser.evalNode("/configuration"));
  7. return configuration;
  8. }
        
        上述方法也很巧妙的用了一个标识来确保配置文件只解析一次,这点技巧也值得我们在开发中学习。通过上述方法我们跟踪到parseConfiguration()方法中;以下内容会涉及到XML文件的解析方式,也即java的基础知识,这里不做过多探讨,有疑问的小伙伴可以自行查找相关资料。
    
          
          
          
          
  1. private void parseConfiguration(XNode root) {
  2. try {
  3. propertiesElement(root.evalNode("properties")); //issue #117 read properties first
  4. typeAliasesElement(root.evalNode("typeAliases"));
  5. pluginElement(root.evalNode("plugins"));
  6. objectFactoryElement(root.evalNode("objectFactory"));
  7. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  8. settingsElement(root.evalNode("settings"));
  9. environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
  10. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  11. typeHandlerElement(root.evalNode("typeHandlers"));
  12. mapperElement(root.evalNode("mappers"));
  13. } catch (Exception e) {
  14. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  15. }
  16. }
        上述方法中根据Mybatis中的可配参数,逐步进行解析,分别对应的是以下几种:
  •  properties 全局参数
  • setting 设置
  • typeAliases 别名。
  • typeHandler 类处理器
  • ObjectFactory 对象
  • plugin 插件
  • environment环境
  • DatabaseIdProvider 数据库标识
  • Mapper映射器
    对以上参数不了解的小伙伴,可以查阅Mybatis基础知识的相关资料。下面我们对每一个参数的解析以及处理进行分别探讨。
     1、properties 
  1. 用于配置全局参数,之后就可以使用${参数名}进行引用。
  2.              
                 
                 
                 
    1. private void propertiesElement(XNode context) throws Exception {
    2. if (context != null) {
    3. Properties defaults = context.getChildrenAsProperties();
    4. String resource = context.getStringAttribute("resource");
    5. String url = context.getStringAttribute("url");
    6. if (resource != null && url != null) {
    7. throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
    8. }
    9. if (resource != null) {
    10. defaults.putAll(Resources.getResourceAsProperties(resource));
    11. } else if (url != null) {
    12. defaults.putAll(Resources.getUrlAsProperties(url));
    13. }
    14. Properties vars = configuration.getVariables();
    15. if (vars != null) {
    16. defaults.putAll(vars);
    17. }
    18. parser.setVariables(defaults);
    19. configuration.setVariables(defaults);
    20. }
    21. }
    Properties defaults = context . getChildrenAsProperties ();
                   这行代码就已经将我们配置的全局参数读到Configuration中了,但又对"url"以及"resource"两个参数进行了特殊处理,因为resource参数对应的是类路径的属性文件,url对应的是指定路径下的属性文件,两个属性文件不能共存,并且这两个属性文件中的属性会覆盖我们在propertis中配置的同名属性。
    2、setting
  1.              
                 
                 
                 
    1. private void settingsElement(XNode context) throws Exception {
    2. if (context != null) {
    3. Properties props = context.getChildrenAsProperties();
    4. // Check that all settings are known to the configuration class
    5. MetaClass metaConfig = MetaClass.forClass(Configuration.class);
    6. for (Object key : props.keySet()) {
    7. if (!metaConfig.hasSetter(String.valueOf(key))) {
    8. throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
    9. }
    10. }
    11. configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    12. configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    13. configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    14. configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    15. configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
    16. configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    17. configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    18. configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    19. configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    20. configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    21. configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    22. configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    23. configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    24. configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    25. configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    26. configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    27. configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    28. configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    29. configuration.setLogPrefix(props.getProperty("logPrefix"));
    30. configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
    31. configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    32. }
    33. }
    setting中可设置的参数较多,我们不过多研究。
    3、typeAliases
        别名的设置有两种方式,一种是包扫描的方式批量设置别名,一种是我们针对每个类自定义别名。
          
           
           
           
           
  1. private void typeAliasesElement(XNode parent) {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
  5. String typeAliasPackage = child.getStringAttribute("name");
  6. configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
  7. } else {
  8. String alias = child.getStringAttribute("alias");
  9. String type = child.getStringAttribute("type");
  10. try {
  11. Class clazz = Resources.classForName(type);
  12. if (alias == null) {
  13. typeAliasRegistry.registerAlias(clazz);
  14. } else {
  15. typeAliasRegistry.registerAlias(alias, clazz);
  16. }
  17. } catch (ClassNotFoundException e) {
  18. throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
  19. }
  20. }
  21. }
  22. }
  23. }

            
            
            
            
  1. public void registerAlias(Class type) {
  2. String alias = type.getSimpleName();
  3. Alias aliasAnnotation = type.getAnnotation(Alias.class);
  4. if (aliasAnnotation != null) {
  5. alias = aliasAnnotation.value();
  6. }
  7. registerAlias(alias, type);
  8. }
        上面的代码我们可以看到如果我们设置了别名,会使用的别名。如果我们没有设置别名,就会用类的名称,第一个字母小写的方式设置别名。
    4、 plugin
        
             
             
             
             
  1. private void pluginElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. String interceptor = child.getStringAttribute("interceptor");
  5. Properties properties = child.getChildrenAsProperties();
  6. Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
  7. interceptorInstance.setProperties(properties);
  8. configuration.addInterceptor(interceptorInstance);
  9. }
  10. }
  11. }
        上述代码可以看到所有配置的插件之后,将其添加到configuration
        
              
              
              
              
  1. public void addInterceptor(Interceptor interceptor) {
  2. interceptorChain.addInterceptor(interceptor);
  3. }
               
               
               
               
  1. public class InterceptorChain {
  2. private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
  3. public Object pluginAll(Object target) {
  4. for (Interceptor interceptor : interceptors) {
  5. target = interceptor.plugin(target);
  6. }
  7. return target;
  8. }
  9. public void addInterceptor(Interceptor interceptor) {
  10. interceptors.add(interceptor);
  11. }
  12. public List<Interceptor> getInterceptors() {
  13. return Collections.unmodifiableList(interceptors);
  14. }
  15. }
插件相当于拦截器,采用责任链模式设计,具体我们在自定义插件实现的时候进行研究。

    5、Mapper
            
          
          
          
          
  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
  5. String mapperPackage = child.getStringAttribute("name");
  6. configuration.addMappers(mapperPackage);
  7. } else {
  8. String resource = child.getStringAttribute("resource");
  9. String url = child.getStringAttribute("url");
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class mapperInterface = Resources.classForName(mapperClass);
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
        上述代码我们可以看到添加映射器Mapper的方法有四种设置方式,根据包名package设置,根据url设置、根据resource设置、根据class设置,最后添加Mapper的方法是 configuration . addMappers ( mapperPackage );
        我们看一下真正添加映射器的方法 
         MapperRegistry注册器中的添加方法。
        
           
           
           
           
  1. public <T> void addMapper(Class<T> type) {
  2. if (type.isInterface()) {
  3. if (hasMapper(type)) {
  4. throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  5. }
  6. boolean loadCompleted = false;
  7. try {
  8. knownMappers.put(type, new MapperProxyFactory<T>(type));
  9. // It's important that the type is added before the parser is run
  10. // otherwise the binding may automatically be attempted by the
  11. // mapper parser. If the type is already known, it won't try.
  12. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  13. parser.parse();
  14. loadCompleted = true;
  15. } finally {
  16. if (!loadCompleted) {
  17. knownMappers.remove(type);
  18. }
  19. }
  20. }
  21. }

        我们很清楚的看到真正添加的是一个Mapper代理工厂对象。当需要使用Mapper的时候会调用getMapper()方法。
    
           
           
           
           
  1. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  2. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  3. if (mapperProxyFactory == null)
  4. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  5. try {
  6. return mapperProxyFactory.newInstance(sqlSession);
  7. } catch (Exception e) {
  8. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  9. }
  10. }
         return  mapperProxyFactory. newInstance (sqlSession) ; 这行代码是真正返回Mapper代理对象的方法。
        
            
            
            
            
  1. protected T newInstance(MapperProxy<T> mapperProxy) {
  2. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  3. }
  4. public T newInstance(SqlSession sqlSession) {
  5. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  6. return newInstance(mapperProxy);
  7. }
  从上面代码我们可以很清晰的看到我们真正操作的是由Mybatis通过JDK动态代理的方式生成的对象,这也解释了为什么我们需要有Mapper接口,具体Mapper的执行过程,我们会在后面的博文中学习。

        由于涉及到的参数有很多,我们主要介绍以上常见的几种,其他的解析源码,大家自行研究。

       以上过程执行完成后,我们就获得了一个SqlSessionFactory对象。

二、使用技术分析
1、与Spring结合过程
    我们通过上面的代码分析可以了解到如果我们需要通过Spring工厂帮我们创建对象,需要实现FactoryBean接口,重现其中的getObject()方法。
2、XML文件解析
    对于配置文件的解析,最基础的技术就是XML解析,所以对于尝试开发自己框架的小伙伴,XML文件解析技术需要熟练掌握
3、基本Java技术
    代码中充斥着大量的反射技术,反射技术的存在也是XML配置文件解析使用的基础。
    动态代理技术,常见的动态代理技术有JDK动态代理,前提是需要有接口,以及CGLIB框架,不需要有接口。
4、使用的设计模式
    基本可以看到的有创建SqlSessionFactory对象时使用的Builder模式
    插件使用的责任链模式。
    当然源码中也使用了不少设计模式,目前我了解的设计模式知识不多,先探讨到这里。

 

你可能感兴趣的:(JAVA,框架)