zoukankan      html  css  js  c++  java
  • MyBaits自动配置原理

    前言

    首先我们建立一个SpringBoot工程,导入mybatis-spring-boot-starter依赖。

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    

    导入后发现这个依赖其实就是帮助我们导入了mybatis需要的依赖,其中和自动配置相关最重要的一个就是mybatis-spring-boot-autoconfigure

    <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
          <groupId>org.mybatis.spring.boot</groupId>
          <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
        </dependency>
      </dependencies>
    

    MyBatis自动配置中是如何工作的

    如上面分析自动配置的关键类我们就从mybatis-spring-boot-autoconfigure开始着手分析。

    QQ截图20210517172159.png

    spring.factories

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,
    org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
    

    从spring.factories文件看到这里通过SPI机制加载了两个类

    • MybatisAutoConfiguration
    • MybatisLanguateDriverAutoConfiguration.

    MybatisAutoConfiguration

    QQ截图20210517172514.png

    //表示这是一个Spring配置类
    @Configuration 
    //这个类需要在classpath中存在SqlSessionFactory和SqlSessionFactoryBean时才生效
    @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) --
    //这个类需要有一个DataSource的Canidate注册到Spring容器中 
    @ConditionalOnSingleCandidate(DataSource.class)
    //使MybatisProperties注解类生效
    @EnableConfigurationProperties({MybatisProperties.class})
    //需要在DataSourceAutoConfiguration和MybatisLanguageDriverAutoConfiguration自动配置之后执行
    @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
    public class MybatisAutoConfiguration implements InitializingBean {
       
    }
    

    MybatisAutoConfiguration#sqlSessionFactory

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    	//创建一个SqlSessionFactoryBean, 在mybatis-spring项目下
        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()));
    	}
        
        //应用Configuration对象
    	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);
    	}
      
        //这里默认会返回一个DefaultSqlSessionFactory对象
    	return factory.getObject();
    }
    

    MybatisAutoConfiguration#sqlSessionTemplate

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    	ExecutorType executorType = this.properties.getExecutorType();
    	return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
    }
    

    到这里也就知道了MyBatis自动配置其实就是替我们完成了SqlSessionFactory和SqlSessionTempate的创建, 省去了自己导入相关依赖和配置相关Bean的麻烦.

    MybatisLanguageDriverAutoConfiguration

    这个类的配置是对各个语言的支持,比如Thymeleaf, Velocity,LegacyVelociy, FreeMarker等视图组件的支持。

    @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() {
            }
        }
    
        @Configuration
        @ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class})
        public static class VelocityConfiguration {
            public VelocityConfiguration() {
            }
        }
    
        @Configuration
        @ConditionalOnClass({Driver.class})
             @ConditionalOnMissingClass({"org.mybatis.scripting.velocity.VelocityLanguageDriverConfig"})
        public static class LegacyVelocityConfiguration {
            public LegacyVelocityConfiguration() {
            }
        }
    
        @Configuration
        @ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class})
        public static class FreeMarkerConfiguration {
            public FreeMarkerConfiguration() {
            }
        }
    
        @Configuration
        @ConditionalOnClass({FreeMarkerLanguageDriver.class})
        @ConditionalOnMissingClass({"org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig"})
        public static class LegacyFreeMarkerConfiguration {
            public LegacyFreeMarkerConfiguration() {
            }
        }
    }
    

    MybatisLanguageDriverAutoConfiguration类在org.mybatis.spring.boot.autoconfigure包下,我删掉了内部静态类下的代码,为了保持这个类看起来更直观

    自定义Mapper是如何被扫描的

    业务开发中,我们是声明接口(Mapper),那么我们自定义的Mapper是如何被扫描的呢, 我们继续顺着MybatisAutoConfiguration代码分析,其内部包含了一个AutoConfiguredMapperScannerRegistrar的内部静态类.

    AutoConfiguredMapperScannerRegistrar

    registerBeanDefinitions

    public static class AutoConfiguredMapperScannerRegistrar 
        implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    	private BeanFactory beanFactory;
    
    	public AutoConfiguredMapperScannerRegistrar() {
    	}
    
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    		if (!AutoConfigurationPackages.has(this.beanFactory)) {
    			
    		} else {
    	        //1.获取到SpringBoot的基础包路径
    			List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
    			
                //2.生成一个BeanDefinition的构造器,用于构建MapperScannerConfigurer的                         //BeanDefinition
    			BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    			builder.addPropertyValue("processPropertyPlaceHolders", true);
                //3.设置@Mapper注解的接口才会被当成Mapper接口
    			builder.addPropertyValue("annotationClass", Mapper.class);
                
    			builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
    			BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
    			//4.获取MapperScannerConfigurer的属性名称
                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:}");
    			}
                //5.这里添加一个MapperScannerConfigurer的BeanDefinition对象,也就是注入一个
                //MapperScannerConfigurer对象
    			registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    		}
    	}
    
    	public void setBeanFactory(BeanFactory beanFactory) {
    		this.beanFactory = beanFactory;
    	}
    }
    

    AutoConfiguredMapperScannerRegistrar类是MybatisAutoConfiguration的内部静态类,位于包org.mybatis.spring.boot.autoconfigure下。

    可以看到这个类实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar接口是Spring用来动态注册Bean的,也就是会向Spring容器中注入一个BeanDefinition, 这个BeanDefinition就是MapperScannerConfigurer。
    ImportBeanDefinitionRegistrar实现类只能通过其他类@Import的方式来加载,通常是配置类或者启动类,所以MybatisAutoConfiguration类下还有一个内部类MapperScannerRegistrarNotFoundConfiguration如下。

    @Configuration
    @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
            
    }
    

    这个方法里最后会调用接口BeanDefinitionRegistry.registerBeanDefinition, beanName是"org.mybatis.spring.mapper.MapperScannerConfigurer", registerBeanDefinition方法实际会调用DefaultListableBeanFactory.registerBeanDefinition。DefaultListableBeanFactory是BeanDefinitionRegistry接口的实现类。
    QQ截图20210518113732.png

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory 
        implements ConfigurableListableBeanFactory, 
                   BeanDefinitionRegistry, 
                   Serializable {
    
    }
    

    AutoConfiguredMapperScannerRegistrar类和MapperScanner注解的作用是一样的,如果你没有通过以下三种配置方式扫描Mapper接口的包路径

    • 配置MapperScannerConfigurer扫描器类型的Spring Bean
    • @Mapper注解
    • <mybatis: scan/>标签

    那么这里就会通过AutoConfiguredMapperScannerRegistrar类添加一个MapperScannerConfigurer扫描器对象,去扫描SpringBoot包设置的基础包路径,也就是启动类的同级目录。 如果设置了@Mapper注解,则会当成Mapper接口解析,那么这里自动配置则不生效。

    MapperScannerConfigurer
    QQ截图20210517175252.png

    MapperScannerConfigurer

    MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor 接口又继承了BeanFactoryPostProcessor接口, 也就是说在MapperScannerConfigurer类里需要实现这两个接口的方法。

    public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
        void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
    }
    
    @FunctionalInterface
    public interface BeanFactoryPostProcessor {
        void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
    }
    

    在MapperScannerConfigurer类里可以看到这里只实现了postProcessBeanDefinitionRegistry。

    QQ截图20210521091303.png

    BeanDefinitionRegistryPostProcessor

    Spring里有两个用来动态注册Bean到容器中(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar)。ImportBeanDefinitionRegistrar上文中有提到。
    BeanDefinitionRegistryPostProcessor接口实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinitionRegistry的后置处理器,用来注册额外的BeanDefinition,postProcessBeanDefinitionRegistry方法会在所有的beanDefinition被加载了,但是所有的Bean还没创建前调用。BeanDefinitionRegistryPostProcessor经常被用来注册BeanFactoryPostProcessor的BeanDefinition。

    postProcessBeanDefinitionRegistry

    public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
        
        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, ",; 	
    "));
        }
    }
    

    MapperScannerConfigurer在包org.mybatis.spring.mapper下.

    这里会调用ClassPathMapperScanner.scan(),而ClassPathMapperScanner又继承了ClassPathBeanDefinitionScanner,所以这里scan()会调用ClassPathBeanDefinitionScanner.scan(), 而ClassPathBeanDefinitionScanner.scan() 第二句代码又调用了this.doScan(basePackages), this.doScan()又调用了ClassPathMapperScanner.doScan(), 而这个方法第一句代码又调用了super.doScan(basePackages),父子类来回互相调用,有点晕头转向的。

    org.mybatis.spring.mapper.ClassPathMapperScanner
    org.springframework.context.annotation.ClassPathBeanDefinitionScanner这个类在spring-context.jar

    ClassPathMapperScanner

    ClassPathBeanDefinitionScanner#scan

    public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }
    
        return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    }
    

    ClassPathMapperScanner#doScan()
    这个方法里在mybatis自动配置算比较重要的一个方法,也就是帮助我们自动配置MapperFactoryBean, 会把根据basePackage注册进Spring容器的BeanDefinition的beanClass设置成MapperFactoryBean。

    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 {
            //这是一个关键方法,会处理已经注册进Spring容器的beanDefinition,也就是会把
            //已经注册进Spring容器的beanDefinitiond的beanClass为MapperFactoryBean
            this.processBeanDefinitions(beanDefinitions);
        }
    
        return beanDefinitions;
    }
    

    ClassPathBeanDefinitionScanner#doScan()

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        String[] var3 = basePackages;
        int var4 = basePackages.length;
        
        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            //这个方法会扫描指定basePackage下被@Mapper注解标注的接口
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            Iterator var8 = candidates.iterator();
    
            while(var8.hasNext()) {
                BeanDefinition candidate = (BeanDefinition)var8.next();
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                //这里获取beanName, 默认值是类名首字母小写
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }
    
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }
                //检查对应的Mapper接口是否被注册进Spring容器中。
                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        
        //这个集合返回以后 Spring容器会将里面的所有内容注册到容器中
        return beanDefinitions;
    }
    

    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);
    		//1.这里把普通接口设置成MapperFactoryBean
    		definition.setBeanClass(this.mapperFactoryBeanClass);
            //2.是否把Mapper接口加入到Mybatis的Config当中去, 这里设置为true
    		definition.getPropertyValues().add("addToConfig", this.addToConfig);
    		definition.setAttribute("factoryBeanObjectType", beanClassName);
    		boolean explicitFactoryUsed = false;
    		
    		//2.从核心容器里获取SqlSessionFactory赋值给MapperFactoryBean
    		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;
    		}
    		
    		//3.从核心容器里获取SqlSessionTemplate赋值给MapperFactoryBean
    		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());
    			}
    		}
    	}
    }
    

    QQ截图20210520174957.png

    再看spring.factories文件

    上面只是完成了MyBatis 自动配置的工作,那么这些自动配置是如何在SpringBoot启动时执行呢? 还记得SpringBoot的EnableAutoConfiguration注解吧,它是利用SpringFactoriesLoader机制加载所有的AutoConfiguration类,所以我们需要把编写好的自动配置类放在META-INF/spring.factories文件中,这也就是我们一开始分析的mybatis-spring-boot-starter-autoconfigure的文件结构的原因。

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,
    org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
    

    总结

    1. mybatis-spring-boot-starter将mybatis需要的依赖全部引入。
    2. starter通过SPI机制引入了一个配置的Class(MyBatisAutoConfiguration)。它负责注册SqlSessionFactory和SqlSessionTemplate到Spring容器中,使用MyBatis开发时绝大部分功能要使用这两个类来完成。
    3. 注入了AutoConfiguredMapperScannerRegistrar这个Bean到Spring容器,它负责将MapperScanner引入到Spring容器,然后MapperScanner会将工程中指定package下的Mapper转化为BeanDefinition并且注册到Spring容器中。
    4. 在开发中使用某个具体的Mapper时,Spring能够从容器中找到这个Mapper对应的BeanDefinition,将其实例化并且注入,这样开发者就可以使用了。
    5. SpringBoot+MyBatis自动配置涉及到Spring中两个自动注册Bean的关键接口(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar),也是我们业务开发中需要重点关注的地方。

    参考链接

    https://www.cnblogs.com/nullifier/p/11967659.html
    https://zhuanlan.zhihu.com/p/30123517
    https://www.cnblogs.com/daxin/p/3545040.html

    博客地址:http://www.cnblogs.com/sword-successful/
    博客版权:本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。
    如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!
    再次感谢您耐心的读完本篇文章。
  • 相关阅读:
    WPF学员管理系统
    dotnet-千星项目OpenAuthNet基于NetCore21的快速开发框架
    MVC5仓库管理系统
    华为设备IPC V200R002C0SPC SDK连接demo
    基础界面
    交通建设项目管理信息化系统
    Apache常见interview
    mamcached+(magent+keepalived高可用)搭建及理论概述
    TCP/IP三次挥手,四次断开(精简)
    简述FTP的主动模式与被动模式(精简)
  • 原文地址:https://www.cnblogs.com/sword-successful/p/14799975.html
Copyright © 2011-2022 走看看