springboot整合mybatis 之后直接注入接口的原理。
1. pom引入
<!-- spring-boot整合mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency>
2. 源码查看
1. spring.factories 引入如下配置
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration, org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
2. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration 源码
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.mybatis.spring.boot.autoconfigure; import org.apache.ibatis.scripting.LanguageDriver; import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver; import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig; import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver; import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig; import org.mybatis.scripting.thymeleaf.SqlGeneratorConfig.DialectConfig; import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig.TemplateFileConfig; import org.mybatis.scripting.velocity.Driver; import org.mybatis.scripting.velocity.VelocityLanguageDriver; import org.mybatis.scripting.velocity.VelocityLanguageDriverConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass({LanguageDriver.class}) public class MybatisLanguageDriverAutoConfiguration { private static final String CONFIGURATION_PROPERTY_PREFIX = "mybatis.scripting-language-driver"; public MybatisLanguageDriverAutoConfiguration() { } @Configuration @ConditionalOnClass({ThymeleafLanguageDriver.class}) public static class ThymeleafConfiguration { public ThymeleafConfiguration() { } @Bean @ConditionalOnMissingBean ThymeleafLanguageDriver thymeleafLanguageDriver(ThymeleafLanguageDriverConfig config) { return new ThymeleafLanguageDriver(config); } @Bean @ConditionalOnMissingBean @ConfigurationProperties("mybatis.scripting-language-driver.thymeleaf") public ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig() { return ThymeleafLanguageDriverConfig.newInstance(); } private static class MetadataThymeleafLanguageDriverConfig extends ThymeleafLanguageDriverConfig { private MetadataThymeleafLanguageDriverConfig() { } @ConfigurationProperties("mybatis.scripting-language-driver.thymeleaf.dialect") public DialectConfig getDialect() { return super.getDialect(); } @ConfigurationProperties("mybatis.scripting-language-driver.thymeleaf.template-file") public TemplateFileConfig getTemplateFile() { return super.getTemplateFile(); } } } @Configuration @ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class}) public static class VelocityConfiguration { public VelocityConfiguration() { } @Bean @ConditionalOnMissingBean VelocityLanguageDriver velocityLanguageDriver(VelocityLanguageDriverConfig config) { return new VelocityLanguageDriver(config); } @Bean @ConditionalOnMissingBean @ConfigurationProperties("mybatis.scripting-language-driver.velocity") public VelocityLanguageDriverConfig velocityLanguageDriverConfig() { return VelocityLanguageDriverConfig.newInstance(); } } @Configuration @ConditionalOnClass({Driver.class}) @ConditionalOnMissingClass({"org.mybatis.scripting.velocity.VelocityLanguageDriverConfig"}) public static class LegacyVelocityConfiguration { public LegacyVelocityConfiguration() { } @Bean @ConditionalOnMissingBean Driver velocityLanguageDriver() { return new Driver(); } } @Configuration @ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class}) public static class FreeMarkerConfiguration { public FreeMarkerConfiguration() { } @Bean @ConditionalOnMissingBean FreeMarkerLanguageDriver freeMarkerLanguageDriver(FreeMarkerLanguageDriverConfig config) { return new FreeMarkerLanguageDriver(config); } @Bean @ConditionalOnMissingBean @ConfigurationProperties("mybatis.scripting-language-driver.freemarker") public FreeMarkerLanguageDriverConfig freeMarkerLanguageDriverConfig() { return FreeMarkerLanguageDriverConfig.newInstance(); } } @Configuration @ConditionalOnClass({FreeMarkerLanguageDriver.class}) @ConditionalOnMissingClass({"org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig"}) public static class LegacyFreeMarkerConfiguration { public LegacyFreeMarkerConfiguration() { } @Bean @ConditionalOnMissingBean FreeMarkerLanguageDriver freeMarkerLanguageDriver() { return new FreeMarkerLanguageDriver(); } } }
3. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 源码
package org.mybatis.spring.boot.autoconfigure; import java.beans.FeatureDescriptor; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.sql.DataSource; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.mapping.DatabaseIdProvider; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.TypeHandler; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.mapper.MapperFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); private final MybatisProperties properties; private final Interceptor[] interceptors; private final TypeHandler[] typeHandlers; private final LanguageDriver[] languageDrivers; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer> configurationCustomizers; public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { this.properties = properties; this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable(); this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable(); this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable(); this.resourceLoader = resourceLoader; this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable(); this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable(); } public void afterPropertiesSet() { this.checkConfigFileExists(); } private void checkConfigFileExists() { if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) { Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation()); Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)"); } } @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } this.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 = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); if (defaultLanguageDriver == null && this.languageDrivers.length == 1) { defaultLanguageDriver = this.languageDrivers[0].getClass(); } } if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); } return factory.getObject(); } private void applyConfiguration(SqlSessionFactoryBean factory) { org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new org.apache.ibatis.session.Configuration(); } if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { Iterator var3 = this.configurationCustomizers.iterator(); while(var3.hasNext()) { ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next(); customizer.customize(configuration); } } factory.setConfiguration(configuration); } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory); } @Configuration @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } } public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; public AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); } else { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (MybatisAutoConfiguration.logger.isDebugEnabled()) { packages.forEach((pkg) -> { MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg); }); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); if (propertyNames.contains("lazyInitialization")) { builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); } if (propertyNames.contains("defaultScope")) { builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); } registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } } public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } } }
所以核心在MybatisAutoConfiguration 这个类上面。这个类注入了几个重要的对象:
(1) SqlSessionFactory mybatis 的会话工厂
1》this.properties.resolveMapperLocations() 这个会解析到所有的Mapper.xml 文件,然后存到org.mybatis.spring.SqlSessionFactoryBean#mapperLocations 属性中
2》 factory.getObject() 会调用到org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory, 这个里面会解析Mapper.xml 文件,生成MappedStatement 对象等
protected SqlSessionFactory buildSqlSessionFactory() throws Exception { XMLConfigBuilder xmlConfigBuilder = null; Configuration targetConfiguration; Optional var10000; 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(), (String)null, this.configurationProperties); targetConfiguration = xmlConfigBuilder.getConfiguration(); } else { LOGGER.debug(() -> { return "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"; }); targetConfiguration = new Configuration(); var10000 = Optional.ofNullable(this.configurationProperties); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::setVariables); } var10000 = Optional.ofNullable(this.objectFactory); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::setObjectFactory); var10000 = Optional.ofNullable(this.objectWrapperFactory); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::setObjectWrapperFactory); var10000 = Optional.ofNullable(this.vfs); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::setVfsImpl); Stream var24; if (StringUtils.hasLength(this.typeAliasesPackage)) { var24 = this.scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream().filter((clazz) -> { return !clazz.isAnonymousClass(); }).filter((clazz) -> { return !clazz.isInterface(); }).filter((clazz) -> { return !clazz.isMemberClass(); }); TypeAliasRegistry var10001 = targetConfiguration.getTypeAliasRegistry(); Objects.requireNonNull(var10001); var24.forEach(var10001::registerAlias); } if (!ObjectUtils.isEmpty(this.typeAliases)) { Stream.of(this.typeAliases).forEach((typeAlias) -> { targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias); LOGGER.debug(() -> { return "Registered type alias: '" + typeAlias + "'"; }); }); } if (!ObjectUtils.isEmpty(this.plugins)) { Stream.of(this.plugins).forEach((plugin) -> { targetConfiguration.addInterceptor(plugin); LOGGER.debug(() -> { return "Registered plugin: '" + plugin + "'"; }); }); } if (StringUtils.hasLength(this.typeHandlersPackage)) { var24 = this.scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter((clazz) -> { return !clazz.isAnonymousClass(); }).filter((clazz) -> { return !clazz.isInterface(); }).filter((clazz) -> { return !Modifier.isAbstract(clazz.getModifiers()); }); TypeHandlerRegistry var25 = targetConfiguration.getTypeHandlerRegistry(); Objects.requireNonNull(var25); var24.forEach(var25::register); } if (!ObjectUtils.isEmpty(this.typeHandlers)) { Stream.of(this.typeHandlers).forEach((typeHandler) -> { targetConfiguration.getTypeHandlerRegistry().register(typeHandler); LOGGER.debug(() -> { return "Registered type handler: '" + typeHandler + "'"; }); }); } targetConfiguration.setDefaultEnumTypeHandler(this.defaultEnumTypeHandler); if (!ObjectUtils.isEmpty(this.scriptingLanguageDrivers)) { Stream.of(this.scriptingLanguageDrivers).forEach((languageDriver) -> { targetConfiguration.getLanguageRegistry().register(languageDriver); LOGGER.debug(() -> { return "Registered scripting language driver: '" + languageDriver + "'"; }); }); } var10000 = Optional.ofNullable(this.defaultScriptingLanguageDriver); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::setDefaultScriptingLanguage); if (this.databaseIdProvider != null) { try { targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException var23) { throw new NestedIOException("Failed getting a databaseId", var23); } } var10000 = Optional.ofNullable(this.cache); Objects.requireNonNull(targetConfiguration); var10000.ifPresent(targetConfiguration::addCache); if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); LOGGER.debug(() -> { return "Parsed configuration file: '" + this.configLocation + "'"; }); } catch (Exception var21) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var21); } finally { ErrorContext.instance().reset(); } } targetConfiguration.setEnvironment(new Environment(this.environment, (TransactionFactory)(this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory), this.dataSource)); if (this.mapperLocations != null) { if (this.mapperLocations.length == 0) { LOGGER.warn(() -> { return "Property 'mapperLocations' was specified but matching resources are not found."; }); } else { Resource[] var3 = this.mapperLocations; int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { Resource mapperLocation = var3[var5]; if (mapperLocation != null) { try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception var19) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19); } finally { ErrorContext.instance().reset(); } LOGGER.debug(() -> { return "Parsed mapper file: '" + mapperLocation + "'"; }); } } } } else { LOGGER.debug(() -> { return "Property 'mapperLocations' was not specified."; }); } return this.sqlSessionFactoryBuilder.build(targetConfiguration); }
(2) SqlSessionTemplate 实际是一个SqlSession 对象,spring对mybatis的SqlSession的使用是由SqlSessionTemplate控制的,在SqlSessionTemplate类中执行SQL语句的SqlSession都是通过sqlSessionProxy来代理执行的。其源码如下:org.mybatis.spring.SqlSessionTemplate
package org.mybatis.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.util.List; import java.util.Map; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.reflection.ExceptionUtil; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.util.Assert; public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true)); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); Assert.notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor()); } public SqlSessionFactory getSqlSessionFactory() { return this.sqlSessionFactory; } public ExecutorType getExecutorType() { return this.executorType; } public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { return this.exceptionTranslator; } public <T> T selectOne(String statement) { return this.sqlSessionProxy.selectOne(statement); } public <T> T selectOne(String statement, Object parameter) { return this.sqlSessionProxy.selectOne(statement, parameter); } public <K, V> Map<K, V> selectMap(String statement, String mapKey) { return this.sqlSessionProxy.selectMap(statement, mapKey); } public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) { return this.sqlSessionProxy.selectMap(statement, parameter, mapKey); } public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds); } public <T> Cursor<T> selectCursor(String statement) { return this.sqlSessionProxy.selectCursor(statement); } public <T> Cursor<T> selectCursor(String statement, Object parameter) { return this.sqlSessionProxy.selectCursor(statement, parameter); } public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds); } public <E> List<E> selectList(String statement) { return this.sqlSessionProxy.selectList(statement); } public <E> List<E> selectList(String statement, Object parameter) { return this.sqlSessionProxy.selectList(statement, parameter); } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.selectList(statement, parameter, rowBounds); } public void select(String statement, ResultHandler handler) { this.sqlSessionProxy.select(statement, handler); } public void select(String statement, Object parameter, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, handler); } public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); } public int insert(String statement) { return this.sqlSessionProxy.insert(statement); } public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } public int update(String statement) { return this.sqlSessionProxy.update(statement); } public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); } public int delete(String statement) { return this.sqlSessionProxy.delete(statement); } public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); } public <T> T getMapper(Class<T> type) { return this.getConfiguration().getMapper(type, this); } public void commit() { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void commit(boolean force) { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void rollback() { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void rollback(boolean force) { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void close() { throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); } public void clearCache() { this.sqlSessionProxy.clearCache(); } public Configuration getConfiguration() { return this.sqlSessionFactory.getConfiguration(); } public Connection getConnection() { return this.sqlSessionProxy.getConnection(); } public List<BatchResult> flushStatements() { return this.sqlSessionProxy.flushStatements(); } public void destroy() throws Exception { } private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } unwrapped = result; } catch (Throwable var11) { unwrapped = ExceptionUtil.unwrapThrowable(var11); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped); if (translated != null) { unwrapped = translated; } } throw (Throwable)unwrapped; } finally { if (sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } return unwrapped; } } }
重点解释:
1》org.mybatis.spring.SqlSessionTemplate#sqlSessionProxy 是一个代理对象,并且走的JDK的动态代理,其实现类如下:org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } unwrapped = result; } catch (Throwable var11) { unwrapped = ExceptionUtil.unwrapThrowable(var11); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped); if (translated != null) { unwrapped = translated; } } throw (Throwable)unwrapped; } finally { if (sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } return unwrapped; } }
所以最终到达sqlSessionTemplate 的请求最终会到达org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke 方法,方法内部首先调用:org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator) 开启一个新的SelSession:(也就是每次都会创建一个新的SqlSession对象)。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sessionFactory, "No SqlSessionFactory specified"); Assert.notNull(executorType, "No ExecutorType specified"); SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } }
(3) AutoConfiguredMapperScannerRegistrar 自动扫描配置类,这里注册了一个对象 MapperScannerConfigurer Mapper扫描类是一个配置类,会扫描所有的Mapper 注解,接下来方法会调用:
1》org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { this.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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(this.lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization)); } if (StringUtils.hasText(this.defaultScope)) { scanner.setDefaultScope(this.defaultScope); } scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; ")); }
2》org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
3》org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> { return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."; }); } else { this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
4》org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { BeanDefinitionRegistry registry = this.getRegistry(); Iterator var4 = beanDefinitions.iterator(); while(var4.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next(); AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition(); boolean scopedProxy = false; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> { return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"); }); scopedProxy = true; } String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> { return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"; }); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); definition.setAttribute("factoryBeanObjectType", beanClassName); 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(() -> { return "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(() -> { return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."; }); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { LOGGER.debug(() -> { return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."; }); definition.setAutowireMode(2); } definition.setLazyInit(this.lazyInitialization); if (!scopedProxy) { if ("singleton".equals(definition.getScope()) && this.defaultScope != null) { definition.setScope(this.defaultScope); } if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } } } }
mapperFactoryBeanClass 是: org.mybatis.spring.mapper.MapperFactoryBean,源码如下:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } protected void checkDaoConfig() { super.checkDaoConfig(); Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = this.getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception var6) { this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6); throw new IllegalArgumentException(var6); } finally { ErrorContext.instance().reset(); } } } public T getObject() throws Exception { return this.getSqlSession().getMapper(this.mapperInterface); } public Class<T> getObjectType() { return this.mapperInterface; } public boolean isSingleton() { return true; } public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return this.mapperInterface; } public void setAddToConfig(boolean addToConfig) { this.addToConfig = addToConfig; } public boolean isAddToConfig() { return this.addToConfig; } }
可以看到getObject 是拿到SqlSession,然后调用 getMapper 获取代理对象。
checkDaoConfig 会调用到 调用到org.apache.ibatis.session.Configuration#addMapper。会走原来MapperAnnotationBuilder 扫描注解的一套机制。
到这里就完成了解析XML,解析Mapper 注解,然后生成代理对象。最终代理对象以及调用走的也是Mybatis 的一套机制。
补充:Springboot 自动整合mybatis 导致mybatis 一级缓存失效
上面代码可以看出来。mybatis和spring结合使用的时候,将原本的DefaultSqlSession替换成了SqlSessionTemplate,并且在SqlSessionTemplate将sqlSession替换成了sqlSessionProxy代理对象,当我们执行sqlSession的方法的时会调用到SqlSessionInterceptor的invoke()方法, 在invoke()方法回次会创建一个新的SqlSession 对象,并且在fianlly中调用了SqlSessionUtils.closeSqlSession()方法将SqlSession关闭了,所以一级缓存就会失效了。