从配置文件研究MyBatis的运行过程
Posted 敲代码的小小酥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从配置文件研究MyBatis的运行过程相关的知识,希望对你有一定的参考价值。
一、Spring整合MyBatis配置文件
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 实例化sqlSessionFactory时需要使用上述配置好的数据源以及SQL映射文件 -->
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描*/mapping/目录下的所有SQL映射的xml文件, 省掉Configuration.xml里的手工配置
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:xxx/*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描*.dao这个包以及它的子包下的所有映射接口类 -->
<property name="basePackage" value="*.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
由配置文件可以看到,往Spring容器中注入了SqlSessionFactoryBean和MapperScannerConfigurer两个类。分析这两个类源码,学习MyBaits运行流程。
二、SqlSessionFactoryBean源码
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent>
可以看到,SqlSessionFactoryBean类实现了三个接口,我们一个一个分析。
首先看FactoryBean接口的getObject()实现方法:
@Override
public SqlSessionFactory getObject() throws Exception
if (this.sqlSessionFactory == null)
afterPropertiesSet();
return this.sqlSessionFactory;
该方法就是返回SqlSessionFactory 对象给Spring容器,如果SqlSessionFactory 是空,则调用InitializingBean的afterPropertiesSet()实现方法,看其源码:
@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方法,创建了SqlSessionFactory对象,我们看buildSqlSession方法源码:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException
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);
if (this.objectFactory != null)
configuration.setObjectFactory(this.objectFactory);
if (this.objectWrapperFactory != null)
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
if (this.vfs != null)
configuration.setVfsImpl(this.vfs);
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();
if (this.transactionFactory == null)
this.transactionFactory = new SpringManagedTransactionFactory();
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
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);
可以看到,buildSqlSession方法中设置了Configuration 对象的各种属性,例如配置文件的读取,plugin插件的加入,xml映射关系的建立等等,总之Configuration对象是MyBaits的大杂烩,基本所有的配置都在这里完成,然后通过Configuration 对象生成SqlSessionFactory对象。
这样,MyBatis就把其组件加入到了Spring容器中。由此可知,MyBatis是通过FactoryBean的方式,将其组件加入到Spring容器中。
buildSqlSessionFactory方法的这段代码,需要注意:
if (this.transactionFactory == null)
this.transactionFactory = new SpringManagedTransactionFactory();
可以看到,这里创建了Spring的事务管理器,说明,跟Spring整合后,MyBatis的事务和连接,都由Spring进行管理。
三、MapperScannerConfigurer类研究
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
可以看到其实现了BeanDefinitionRegistryPostProcessor接口,说明可以注册BeanDefinition对象,看其注册方法的实现:
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));
可以看到注册进来了扫描类,重点看scan方法里的doScan方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages)
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
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 definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
return beanDefinitions;
这里注册进来的类,是MapperFactoryBean类型的对象,我们看MapperFactoryBean类的源码
四、MapperFactoryBean类源码
该类将MyBatis的Mapper接口封装进来,封装到如下属性中:
private Class<T> mapperInterface;
我们看这个类的实现和继承关系:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
可知该类实现了FactoryBean接口,看其getObject方法:
@Override
public T getObject() throws Exception
return getSqlSession().getMapper(this.mapperInterface);
- getSqlSession方法研究:
很显然这个方法是通过接口,返回一个接口的实现类。也就是说,Mapper接口的实现类,在这里创建。我们看其是如何创建的。看getSqlsession()方法:
sqlSession的初始化在MapperFactoryBean类的父类SqlSessionDaoSupport中完成,SqlSessionDaoSupport源码如下:
/**
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.spring.support;
import static org.springframework.util.Assert.notNull;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.以上是关于从配置文件研究MyBatis的运行过程的主要内容,如果未能解决你的问题,请参考以下文章