Mybatis源码解析之Spring集成mybatis-spring分析

 

前面的几篇相关博客都针对原生的mybatis进行解析,从本文开始将针对mybatis和spring的集成进行解析。若无特殊说明,所有的源码都基于org.mybatis:mybatis-spring:1.3.1的jar包

一、jar包介绍

mybatis-spring是spring与mybatis的集成jar包,因此对spring和mybatis的相关jar包都存在依赖。
mybatis-spring的jar包依赖

二、配置文件

在spring中集成mybatis框架,主要需要对以下两者进行bean注入。

1. SqlSessionFactory

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

SqlSessionFactory通过SqlSessionFactoryBeanFactory注入,其中的configLocation指定mybatis的配置文件,datasource指定数据源,我们还可以通过property注入其他的一些属性,不在意义列举,通过后续的源码解析会有一定了解。

2. mapper

除了SqlSessionFactory外,我们还有必要将mapper接口进行注入,可以通过注解、配置文件等多种方式注入,此处分别以MapperFactoryBean和MapperScannerConfigurer两种方式为例。

//MapperFactoryBean单个注入
<bean id="xxxMappper" class="org.mybatis.Spring.mapper.MapperFactoryBean">
           <property name="mapperInterface" value="com.xxx.mapper.xxxMapper">
           <peroperty name="sqlSessionFactory"  value="sqlSessionFactory">
</bean>
//MapperScannerConfigurer路径扫描批量注入
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.xxx.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

三、SqlSessionFactoryBean

SqlSessionFactoryBean实现类FactoryBean和InitializingBean接口,基于spring的了解我们明白其注入的是getObject()方法的返回值,跟踪方法的调用,我们可以发现核心逻辑在SqlSessionFactoryBean#buildSqlSessionFactory()方法中。

1. SqlSessionFactoryBean#buildSqlSessionFactory()

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

   Configuration 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) {
     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);
     }
   }
	//objectFactory配置
   if (this.objectFactory != null) {
     configuration.setObjectFactory(this.objectFactory);
   }
	//objectWrapperFactory 配置
   if (this.objectWrapperFactory != null) {
     configuration.setObjectWrapperFactory(this.objectWrapperFactory);
   }
	//vfs配置
   if (this.vfs != null) {
     configuration.setVfsImpl(this.vfs);
   }
	
	//typeAliasesPackage配置,注册aliase
   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");
       }
     }
   }
	
	//typeAliases配置,注册aliase
   if (!isEmpty(this.typeAliases)) {
     for (Class<?> typeAlias : this.typeAliases) {
       configuration.getTypeAliasRegistry().registerAlias(typeAlias);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered type alias: '" + typeAlias + "'");
       }
     }
   }
	//插件plugin配置
   if (!isEmpty(this.plugins)) {
     for (Interceptor plugin : this.plugins) {
       configuration.addInterceptor(plugin);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered plugin: '" + plugin + "'");
       }
     }
   }
	
	//typeHandlersPackage配置,注册typeHandlers
   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");
       }
     }
   }

	//typeHandlersP配置,注册typeHandlers
   if (!isEmpty(this.typeHandlers)) {
     for (TypeHandler<?> typeHandler : this.typeHandlers) {
       configuration.getTypeHandlerRegistry().register(typeHandler);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered type handler: '" + typeHandler + "'");
       }
     }
   }
	
	//databaseIdProvider 
   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();
     }
   }
	
	//transactionFactory 
   if (this.transactionFactory == null) {
   	//事务默认采用SpringManagedTransaction
     this.transactionFactory = new SpringManagedTransactionFactory();
   }
	//environment
   configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
	//mapperLocations
   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");
     }
   }

   return this.sqlSessionFactoryBuilder.build(configuration);
 }

可以看到,其作用就是根据配置文件中的property属性生成mybatis的configuration对象,并组装到sqlSessionFactory中,这一点相当于前面介绍的原生的mybatis的初始化过程。
其中,datasource属性和configLocation(configuration、configurationProperties)和mapperLocations是其中比较基础也比较重要的属性配置,另外,当配置中未指定事务时,mybatis-spring默认采用SpringManagedTransaction。

四、SpringManagedTransaction

1. 类图

Trasaction类图
可以看到,相比于原生mybatis,mybatis-spring提供了SpringManagedTransaction并作为默认的事务机制。
顾名思义,SpringManagedTransaction将mybatis的事务管理委托给spring事务进行处理。

2. SpringManagedTransaction#openConnection()

private void openConnection() throws SQLException {
 this.connection = DataSourceUtils.getConnection(this.dataSource);
 this.autoCommit = this.connection.getAutoCommit();
 this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

 if (LOGGER.isDebugEnabled()) {
   LOGGER.debug(
       "JDBC Connection ["
           + this.connection
           + "] will"
           + (this.isConnectionTransactional ? " " : " not ")
           + "be managed by Spring");
 }

获取connection的逻辑交给了spring-jdbc的工具类DataSourceUtils。

3. DataSourceUtils#doGetConnection(DataSource)

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
	Assert.notNull(dataSource, "No DataSource specified");
	//从县城获取ConnectionHolder
	ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
	if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
		//请求,计数加1
		conHolder.requested();
		if (!conHolder.hasConnection()) {
			logger.debug("Fetching resumed JDBC Connection from DataSource");
			//从数据库获取连接
			conHolder.setConnection(dataSource.getConnection());
		}
		return conHolder.getConnection();
	}
	// Else we either got no holder or an empty thread-bound holder here.

	logger.debug("Fetching JDBC Connection from DataSource");
	Connection con = dataSource.getConnection();
	
	//事务同步是否处于active状态
	if (TransactionSynchronizationManager.isSynchronizationActive()) {
		logger.debug("Registering transaction synchronization for JDBC Connection");
		// Use same Connection for further JDBC actions within the transaction.
		// Thread-bound object will get removed by synchronization at transaction completion.
		ConnectionHolder holderToUse = conHolder;
		if (holderToUse == null) {
			holderToUse = new ConnectionHolder(con);
		}
		else {
			holderToUse.setConnection(con);
		}
		holderToUse.requested();
		当前线程注册新的同步事务
		TransactionSynchronizationManager.registerSynchronization(
				new ConnectionSynchronization(holderToUse, dataSource));
		holderToUse.setSynchronizedWithTransaction(true);
		if (holderToUse != conHolder) {
			//绑定
			TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
		}
	}

	return con;
}

DataSourceUtils#getConnection(DataSource)又将具体逻辑交给了doGetConnection(DataSource)方法。
spring会将事务和线程绑定,因此直接从线程中获取连接,不存在再从数据源申请连接,并绑定到线程中。
并且,ConnectionHolder维护着一个引用计数器,每次获取连接时,会增加一次计数,释放连接(doReleaseConnection)时再将计数减少。

五、MapperFactoryBean

MapperFacotryBean同样实现了FactoryBean和initializingBean接口,注入IOC容器的同样是getObject()方法的返回值。

1. DaoSupport#afterPropertiesSet()

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig();

	// Let concrete implementations initialize themselves.
	try {
		initDao();
	}
	catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}

MapperFactoryBean并没有重写afterPropertiesSet方法,其使用的是父类DaoSupport的方法,先checkDaoConfig()方法检查再initDao()进行初始化。

2. MapperFactoryBean#checkDaoConfig()

protected void checkDaoConfig() {
  super.checkDaoConfig();

  notNull(this.mapperInterface, "Property 'mapperInterface' is required");

  Configuration configuration = getSqlSession().getConfiguration();
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
      configuration.addMapper(this.mapperInterface);
    } catch (Exception e) {
      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
      throw new IllegalArgumentException(e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

addToConfig为true时(默认为true),如果mapper接口没有被配置加入configuration中,手动加入。

3. DaoSupport#initDao()

protected void initDao() throws Exception {
}

MapperFactory同样没有重写initDao()方法,而在DaoSupport中也只是个空方法。

4. MapperFactoryBean#getObject()

@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

可以看到,返回的是mapper接口的代理类对象。

六、MapperScannerConfiguer

1. 类图

MapperScannerConfiguer 类图

2.MapperScannerConfiguer#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  scanner.registerFilters();
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

可以看到在方法中构建了ClassPathMapperScanner对象进行扫描。
此处还会通过processPropertyPlaceHolders()方法手动引入properties属性。
BeanDefinitionRegistries会在应用启动的时候调用,并且会早于BeanFactoryPostProcessors的调用,这就意味着PropertyResourceConfigurers还没有被加载所以任何对于属性文件的引用将会失效,为了避免这种情况的发生,此方法手动找出定义的PropertyResourceConfigurers并进行提前调用以保证对于属性的引用可以正常工作”。

3. ClassPathMapperScanner#registerFilters()

public void registerFilters() {
  boolean acceptAllInterfaces = true;

  // if specified, use the given annotation and / or marker interface
  if (this.annotationClass != null) {
    addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
    acceptAllInterfaces = false;
  }

  // override AssignableTypeFilter to ignore matches on the actual marker interface
  if (this.markerInterface != null) {
    addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
      @Override
      protected boolean matchClassName(String className) {
        return false;
      }
    });
    acceptAllInterfaces = false;
  }

  if (acceptAllInterfaces) {
    // default include filter that accepts all classes
    addIncludeFilter(new TypeFilter() {
      @Override
      public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return true;
      }
    });
  }

  // exclude package-info.java
  addExcludeFilter(new TypeFilter() {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
      String className = metadataReader.getClassMetadata().getClassName();
      return className.endsWith("package-info");
    }
  });
}

可以看到,ClassPathMapperScanner根据annotationClass和markerInterface判断是是否匹配,并需要排除package-info.java。

4. ClassPathMapperScanner#doScan(String …)

 @Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

  if (beanDefinitions.isEmpty()) {
    logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  } else {
    processBeanDefinitions(beanDefinitions);
  }

  return beanDefinitions;
}

ClassPathMapperScanner是ClassPathBeanDefinitionScanner的子类,扫描的核心逻辑委托给了doScan方法。
方法中,根据父类的doScan找出匹配的类,然后交给processBeanDefinitions注入IOC容器。

5. ClassPathBeanDefinitionScanner#doScan(String …)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
	//遍历basePackages进行扫描
	for (String basePackage : basePackages) {
		//找出匹配的类
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
			//封装成BeanDefinitionHolder 对象
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

该方法时spring通用的扫描bean的方法,不展开分析。

6. ClassPathMapperScanner#processBeanDefinition(Set)

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
   GenericBeanDefinition definition;
   for (BeanDefinitionHolder holder : beanDefinitions) {
     definition = (GenericBeanDefinition) holder.getBeanDefinition();

     if (logger.isDebugEnabled()) {
       logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
         + "' and '" + definition.getBeanClassName() + "' mapperInterface");
     }

     // the mapper interface is the original class of the bean
     // but, the actual class of the bean is MapperFactoryBean
     definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
     definition.setBeanClass(this.mapperFactoryBean.getClass());

     definition.getPropertyValues().add("addToConfig", this.addToConfig);

     boolean explicitFactoryUsed = false;
     if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
       definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionFactory != null) {
       definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
       explicitFactoryUsed = true;
     }

     if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
       if (explicitFactoryUsed) {
         logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionTemplate != null) {
       if (explicitFactoryUsed) {
         logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
       explicitFactoryUsed = true;
     }

     if (!explicitFactoryUsed) {
       if (logger.isDebugEnabled()) {
         logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
       }
       definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
     }
   }
 }

可以看到,方法中根据MapperScannerConfigurer的属性将扫描结果封装成GenericBeanDefinition。
重点下面两行代码

// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());

可以看到注入的bean的类型是MapperFactoryBean,扫描出来的类只作为MapperFactoryBean的构造器参数,这一点和MapperFactoryBean的构造器方法相吻合。

public MapperFactoryBean(Class<T> mapperInterface) {
 this.mapperInterface = mapperInterface;
}

最终又回到了我们前面分析的MapperFactoryBean的注入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值