Mybatis源码分析
Posted lisin-lee-cooper
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis源码分析相关的知识,希望对你有一定的参考价值。
一.Mybatis 自动配置装配类 MybatisAutoConfiguration ,需要依赖SqlSessionFactory
@org.springframework.context.annotation.Configuration
@ConditionalOnClass( SqlSessionFactory.class, SqlSessionFactoryBean.class )
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter( DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class )
public class MybatisAutoConfiguration implements InitializingBean
二. SqlSessionFactoryBean 创建 SqlSessionFactory 类
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
// 1. 创建SqlSessionFactoryBean
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
// 2. 设置mybatis xml 配置文件(Location of MyBatis xml config file)
if (StringUtils.hasText(this.properties.getConfigLocation()))
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null)
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
if (!ObjectUtils.isEmpty(this.interceptors))
factory.setPlugins(this.interceptors);
if (this.databaseIdProvider != null)
factory.setDatabaseIdProvider(this.databaseIdProvider);
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage()))
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
if (this.properties.getTypeAliasesSuperType() != null)
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage()))
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
if (!ObjectUtils.isEmpty(this.typeHandlers))
factory.setTypeHandlers(this.typeHandlers);
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations()))
factory.setMapperLocations(this.properties.resolveMapperLocations());
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers))
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1)
defaultLanguageDriver = this.languageDrivers[0].getClass();
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver"))
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
applySqlSessionFactoryBeanCustomizers(factory);
return factory.getObject();
三 factory.getObject() 获取真正的 SqlSessionFactory
SqlSessionFactoryBean 没有交给Spring容器管理,是new 出来的,所以需要手动调用触发
@Override
public SqlSessionFactory getObject() throws Exception
if (this.sqlSessionFactory == null)
afterPropertiesSet();
return this.sqlSessionFactory;
@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();
主要是解析configuration ,最后构造方法创建SqlSessionFactory
protected SqlSessionFactory buildSqlSessionFactory() throws Exception
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null)
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null)
targetConfiguration.setVariables(this.configurationProperties);
else if (this.configurationProperties != null)
targetConfiguration.getVariables().putAll(this.configurationProperties);
else if (this.configLocation != null)
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
else
LOGGER.debug(
() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
if (hasLength(this.typeAliasesPackage))
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
if (!isEmpty(this.typeAliases))
Stream.of(this.typeAliases).forEach(typeAlias ->
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
);
if (!isEmpty(this.plugins))
Stream.of(this.plugins).forEach(plugin ->
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
);
if (hasLength(this.typeHandlersPackage))
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
if (!isEmpty(this.typeHandlers))
Stream.of(this.typeHandlers).forEach(typeHandler ->
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
);
targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
if (!isEmpty(this.scriptingLanguageDrivers))
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver ->
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
);
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
if (this.databaseIdProvider != null) // fix #64 set databaseId before parse mapper xmls
try
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
catch (SQLException e)
throw new NestedIOException("Failed getting a databaseId", e);
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null)
try
xmlConfigBuilder.parse();
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();
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
if (this.mapperLocations != null)
if (this.mapperLocations.length == 0)
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
else
for (Resource mapperLocation : this.mapperLocations)
if (mapperLocation == null)
continue;
try
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
catch (Exception e)
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
finally
ErrorContext.instance().reset();
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
else
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
构建 DefaultSqlSessionFactory,并传入configuration
public SqlSessionFactory build(Configuration config)
return new DefaultSqlSessionFactory(config);
三. Mapper 接口生成代理类执行接口方法
Mapper 被MapperProxy代理,执行InvocationHandler接口的Invoke方法
执行invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable
return mapperMethod.execute(sqlSession, args);
执行MapperMethod 的execute方法
public Object execute(SqlSession sqlSession, Object[] args)
Object result;
switch (command.getType())
case INSERT:
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
case UPDATE:
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
case DELETE:
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
case SELECT:
if (method.returnsVoid() && method.hasResultHandler(以上是关于Mybatis源码分析的主要内容,如果未能解决你的问题,请参考以下文章