• notice
  • Congratulations on the launch of the Sought Tech site

MyBaits automatic configuration principle

foreword

First, we create a SpringBoot project and import the mybatis-spring-boot-starter dependency.

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

After importing, I found that this dependency actually helped us import the dependencies required by mybatis. The most important one related to automatic configuration is 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>

How MyBatis Auto Configuration Works

For example, we analyze the key classes of automatic configuration above, and start the analysis from mybatis-spring-boot-autoconfigure.

QQ screenshot 20210517172159.png

spring.factories

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

From the spring.factories file, see here that two classes are loaded through the SPI mechanism

  • MybatisAutoConfiguration

  • MybatisLanguateDriverAutoConfiguration.

MybatisAutoConfiguration

QQ screenshot 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

@[email protected] 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

@[email protected] SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {	ExecutorType executorType = this.properties.getExecutorType();	return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);}

At this point, we also know that MyBatis automatic configuration actually completes the creation of SqlSessionFactory and SqlSessionTempate for us, saving the trouble of importing related dependencies and configuring related beans.

MybatisLanguageDriverAutoConfiguration

The configuration of this class is to support various languages, such as Thymeleaf, Velocity, LegacyVelociy, FreeMarker and other view components.

@[email protected]({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() {
        }
    }}

The MybatisLanguageDriverAutoConfiguration class is under the org.mybatis.spring.boot.autoconfigure package, I deleted the code under the internal static class, in order to keep this class looking more intuitive

How custom Mappers are scanned

In business development, we are declaring the interface (Mapper), so how is our custom Mapper scanned? We continue to follow the code analysis of MybatisAutoConfiguration, which contains an internal static class of 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;
	}}

The AutoConfiguredMapperScannerRegistrar class is an internal static class of MybatisAutoConfiguration, located under the package org.mybatis.spring.boot.autoconfigure.

You can see that this class implements the ImportBeanDefinitionRegistrar interface. The ImportBeanDefinitionRegistrar interface is used by Spring to dynamically register beans, that is, a BeanDefinition will be injected into the Spring container. This BeanDefinition is MapperScannerConfigurer.
The ImportBeanDefinitionRegistrar implementation class can only be loaded by other classes @Import, usually the configuration class or the startup class, so there is an internal class MapperScannerRegistrarNotFoundConfiguration under the MybatisAutoConfiguration class as follows.

@[email protected]({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
        }

In this method, the interface BeanDefinitionRegistry.registerBeanDefinition will be called finally, the beanName is "org.mybatis.spring.mapper.MapperScannerConfigurer", and the registerBeanDefinition method will actually call DefaultListableBeanFactory.registerBeanDefinition. DefaultListableBeanFactory is the implementation class of the BeanDefinitionRegistry interface.
QQ screenshot 20210518113732.png

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

The AutoConfiguredMapperScannerRegistrar class has the same function as the MapperScanner annotation. If you do not scan the package path of the Mapper interface through the following three configuration methods

  • Configure the Spring Bean for the MapperScannerConfigurer scanner type

  • @Mapper annotation

  • <mybatis: scan/> tag

Then, a MapperScannerConfigurer scanner object will be added through the AutoConfiguredMapperScannerRegistrar class to scan the basic package path set by the SpringBoot package, which is the same level directory of the startup class. If the @Mapper annotation is set, it will be parsed as the Mapper interface, and the automatic configuration here will not take effect.

MapperScannerConfigurer
QQ screenshot 20210517175252.png

MapperScannerConfigurer

MapperScannerConfigurer implements the BeanDefinitionRegistryPostProcessor interface, and the BeanDefinitionRegistryPostProcessor interface inherits the BeanFactoryPostProcessor interface, which means that the methods of these two interfaces need to be implemented in the MapperScannerConfigurer class.

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

In the MapperScannerConfigurer class, you can see that only postProcessBeanDefinitionRegistry is implemented here.

QQ screenshot 20210521091303.png

BeanDefinitionRegistryPostProcessor

Spring has two used to dynamically register beans to the container (BeanDefinitionRegistryPostProcessor and ImportBeanDefinitionRegistrar). ImportBeanDefinitionRegistrar is mentioned above.
The BeanDefinitionRegistryPostProcessor interface implements the BeanFactoryPostProcessor interface, which is the post-processor of the BeanDefinitionRegistry of the Spring framework. It is used to register additional BeanDefinitions. The postProcessBeanDefinitionRegistry method will be called when all beanDefinitions are loaded, but all beans have not been created. BeanDefinitionRegistryPostProcessor is often used to register BeanDefinition of BeanFactoryPostProcessor.

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, ",; \t\n"));
    }}

MapperScannerConfigurer is under package org.mybatis.spring.mapper.

ClassPathMapperScanner.scan() will be called here, and ClassPathMapperScanner inherits ClassPathBeanDefinitionScanner, so here scan() will call ClassPathBeanDefinitionScanner.scan(), and ClassPathBeanDefinitionScanner.scan() The second code calls this.doScan(basePackages), this .doScan() calls ClassPathMapperScanner.doScan(), and the first code of this method calls super.doScan(basePackages). The parent and child classes call each other back and forth, which is a bit confusing.

org.mybatis.spring.mapper.ClassPathMapperScanner
org.springframework.context.annotation.ClassPathBeanDefinitionScanner This class is in 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;}

In this method, ClassPathMapperScanner#doScan()
is an important method in mybatis automatic configuration, that is, it helps us to automatically configure MapperFactoryBean, which will set the beanClass of the BeanDefinition registered in the Spring container according to basePackage to 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 screenshot 20210520174957.png

Look at the spring.factories file again

The above just completes the automatic configuration of MyBatis, so how are these automatic configurations performed when SpringBoot starts? Remember SpringBoot's EnableAutoConfiguration annotation, which uses the SpringFactoriesLoader mechanism to load all AutoConfiguration classes, so we need to put the prepared auto-configuration classes in the META-INF/spring.factories file, which is the mybatis we analyzed at the beginning The reason for the file structure of -spring-boot-starter-autoconfigure.

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

Summarize

  1. mybatis-spring-boot-starter introduces all the dependencies required by mybatis.

  2. The starter introduces a configured Class (MyBatisAutoConfiguration) through the SPI mechanism. It is responsible for registering SqlSessionFactory and SqlSessionTemplate in the Spring container. Most of the functions are done using these two classes when developing with MyBatis.

  3. The AutoConfiguredMapperScannerRegistrar bean is injected into the Spring container, which is responsible for introducing MapperScanner into the Spring container, and then MapperScanner converts the Mapper under the specified package in the project into a BeanDefinition and registers it in the Spring container.

  4. When using a specific Mapper in development, Spring can find the BeanDefinition corresponding to the Mapper from the container, instantiate and inject it, so that developers can use it.

  5. SpringBoot+MyBatis auto-configuration involves two key interfaces for automatically registering beans in Spring ( BeanDefinitionRegistryPostProcessor and ImportBeanDefinitionRegistrar ), which are also the places we need to focus on in our business development.

Reference link

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


Tags

Technical otaku

Sought technology together

Related Topic

0 Comments

Leave a Reply

+