zoukankan      html  css  js  c++  java
  • Springboot自动整合mybatis原理

      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);
        }
    View Code

    (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;
            }
        }
    }
    View Code

    重点解释:

    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, ",; 	
    "));
        }
    View Code

    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);
        }
    View Code

    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;
        }
    View Code

    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());
                    }
                }
            }
    
        }
    View Code

      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;
        }
    }
    View Code

      可以看到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关闭了,所以一级缓存就会失效了。

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    使用electron+vue开发一个跨平台todolist(便签)桌面应用
    初识ABP vNext(12):模块的独立运行与托管
    【老孟Flutter】41个酷炫的 Loading 组件库
    【Flutter 实战】酷炫的开关动画效果
    【老孟Flutter】自定义文本步进组件
    【老孟Flutter】6种极大提升Flutter开发效率的工具包
    【Flutter 实战】pubspec.yaml 配置文件详解
    Flutter 1.22版本新增的Button
    【Flutter 混合开发】添加 Flutter 到 iOS
    【Flutter 混合开发】添加 Flutter 到 Android Fragment
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15231402.html
Copyright © 2011-2022 走看看