Springboot integrates mybatis source code analysis

purpose

In the last blog, I talked about two ways of integration, one of the key words is

 1. 在mapper接口(dao接口)中,使用@Mapper注解,这种方式,无需使用配置类,无需使用@MapperScan注解,即可整合
 2. 在mapper接口中,使用@Repository注解或者不添加任务注解,在全配置类上添加@MapperScan注解,并指定要扫描的包
这篇博客, 我说下这两种整合方式的原理

Principle analysis

In the integration,
if the @MapperScan annotation is used, then: in the annotation, the MapperScannerRegistrar class will be introduced through the @Import annotation, which implements ImportBeanDefinitionRegistrar, so it will scan when spring scans the package
Go to this method and execute the corresponding registerBeanDefinitions method. In this method, the scan of the mapper interface is completed; if the second method is used, the @Mapper annotation is completed by an internal class of MybatisAutoConfiguration

There is another difference between these two methods: the first method is done in the classes in the spring-mybatis.jar package; the second method is done in the mybatis-spring-boot-autoconfigure.jar package

What is integration, I think it is just one sentence, hand over the mapper interface of mybatis to spring management.
The following are the processing chains corresponding to the first and second methods mentioned yesterday.

# 这是第一种方式:通过@MapperScan注解
org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.annotation.AnnotationAttributes, org.springframework.beans.factory.support.BeanDefinitionRegistry)
	org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
		org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
			org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents


# 第二种方式  通过@Mapper注解
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar
	org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
		org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
			org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents

Let’s talk about the second way first:
This is the internal class of MybatisAutoConfiguration, which is used to determine whether it is necessary to inject @Mapper annotations and scan the processing class of the mapper interface.

下面这个内部类的其中一个注解是比较巧妙的
@Configuration
@Import({
    
    MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({
    
    MapperFactoryBean.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    
    
    public MapperScannerRegistrarNotFoundConfiguration() {
    
    
    }
    public void afterPropertiesSet() {
    
    
        MybatisAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
}

@ConditionalOnMissingBean({MapperFactoryBean.class})
This annotation means: If there is a MapperFactoryBean in the spring container, this class will not take effect, and the corresponding annotations will not take effect. (
That's what it means) When will the MapperFactoryvBean object exist in the spring container? It’s very simple, just add a @MapperScan annotation to the configuration class, because this annotation will get all the mapper interfaces (dao interfaces) under the current package when scanning the classes under the package, and set the mapper interface’s The beanClass of the beanDefinition object is set to MapperFactoryBean;

org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions方法中

definition.setBeanClass(this.mapperFactoryBean.getClass());

Here is how to scan, let's not go into details, I will write a separate blog to describe the details of mybatis scanning, here we only need to know, if the @MapperScan annotation is added, the scanned mapper interface is in spring BeanClass are all MapperFactoryBean objects; the reason why the beanClass of the mapper interface is set to MapperFactoryBean is to generate the proxy object of the interface. When the service injects the dao interface, the proxy object of dao will be injected into the service. The proxy object is in MapperFactoryBean. Generated in the getObject() method

Here we know that if we scan the mapper interface through the @MapperScan annotation, the internal class AutoConfiguredMapperScannerRegistrar of MybatisAutoConfiguration will not take effect and will not scan

So why do you say that if you don't add the @MapperScan annotation, you must use the @Mapper annotation to decorate the mapper interface? Because there is such a line of code in 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()) {
    
    
            Iterator var4 = packages.iterator();

            while(var4.hasNext()) {
    
    
                String pkg = (String)var4.next();
                MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
            }
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        if (this.resourceLoader != null) {
    
    
            scanner.setResourceLoader(this.resourceLoader);
        }

				# 这里这行代码
        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(packages));
    }
}

The above line of code will set mapper.class to annotationClass, and then add annotationClass to includeFilters in the
sacnner.registerFilters( ) method. addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
about includeFilters and excludeFilters, in the previous blog The principle of scanning beans is mentioned in the article . The general meaning is: After
spring scans all the classes under the package, it needs to be filtered. Not all classes need to be injected into the spring container. When filtering, There are two layers of filtering, the
first layer: filtering according to includeFilters and excludeFilters, those that meet includeFilters can be injected, and those that meet excludeFilters are the
second layer that does not need to be injected : because spring and mybatis are scanning classes and interfaces respectively, so, Is judged by different conditions

一、spring是调用的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition)

		protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    
    
			AnnotationMetadata metadata = beanDefinition.getMetadata();
			/**
			 * spring在扫描普通bean,是用的这个方法,mybatis对该方法进行了扩展,mybatis判断一个beanDefinition是否要放到beanDefinitionMap中,是判断当前是否是接口,是否是顶级类(org.mybatis.spring.mapper.ClassPathMapperScanner#isCandidateComponent)
			 *
			 * isIndependent:当前类是否独立(顶级类或者嵌套类)
			 * isConcrete: 不是接口,不是抽象类,就返回true
			 * 如果bean是抽象类,且添加了@LookUp注解,也可以注入
			 * 使用抽象类+@LookUp注解,可以解决单实例bean依赖原型bean的问题,这里在spring官方文档中应该也有说明
			 */
			return (metadata.isIndependent() && (metadata.isConcrete() ||
					(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
		}


		ClassPathMapperScanner继承了ClassPathBeanDefinitionScanner
		ClassPathBeanDefinitionScanner继承了ClassPathScanningCandidateComponentProvider

二、mybatis是通过org.mybatis.spring.mapper.ClassPathMapperScanner#isCandidateComponent
		@Override
		protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    
    
		  // 判断beanDefinition是否是接口,或者是否是顶级类
		  return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
		}

Here is the inheritance relationship of ClassPathMapperScanner, that is to say, when scanning beans, mybatis's own ClassPathMapperScanner inherits spring's ClassPathMapperScanner.
Insert picture description here
So, to summarize:
if we do not use the MapperScan annotation, we must use the @Mapper annotation to modify the mapper interface, because If the @MapperScan annotation is not used, the scanning is done by AutoConfiguredMapperScannerRegistrar. When scanning this class, includeFilters is specified, which is Mapper.class; if our mapper interface is not annotated with @Mapper, then all classes under the package will be scanned After that, when the first layer is filtered, it will be filtered out

If we are using the @MapperScan annotation, there is no need to add the @Mapper annotation, because the @MapperScan annotation will introduce MapperScannerRegistrar through the import annotation, and then in the MapperScannerRegistrar class, the interface will be scanned. When scanning, it is not in the includeFilters Specify that @Mapper annotations must be used; therefore, @Mapper annotations may not be added

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/107536970