Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

宅哥聊构架 后端 2024-10-09

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

1.简单项目:

我这里有一个简单的Springboot的Web项目,需要添加Springboot整合mybatis或者是mybatisPlus的依赖,这里我就以mybatis为例了,mybatisPlus跟mybatis是差不多的,首先添加依赖:xml

代码解读
复制代码
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

Springboot的主启动类:使用MapperScan注解配置了一个包扫描的路径。less

代码解读
复制代码
@SpringBootApplication @MapperScan("com.atlx") public class Springboot2DemoApplication { public static void main(String[] args) { SpringApplication.run(Springboot2DemoApplication.class, args); } }

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

如果我们不使用@MapperScan注解配置包扫描的路径的话,Springboot在启动的时候也会去扫描Mapper接口,但是扫描的路径跟Springboot启动扫描类生成BeanDefinition的路径是一样的,并且还只会去扫描加了@Mapper注解的类。但是如果我们使用了@MapperScan注解并且配置了扫描的路径的话那么Springboot就会根据我们配置的路径去扫描Mapper接口。

所以下面我们的分析就是根据有没有加@MapperScan注解来分析。

2.没有加@MapperScan注解:

如果一个Springboot项目中没有加@MapperScan注解的话,那么在扫描Mapper接口的时候Springboot回去扫描加了@Mapper注解的接口,下面我们通过源码进行分析:
首先在idea中找到Springboot整合mybatis的依赖包:

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码​编辑

找到里面对应的spring.factories文件:ini

代码解读
复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

它里面有对应的springboot整合mybatis的自动配置文件:MybatisAutoConfiguration,进入自动配置文件:里面有一个非常核心的内部类: MapperScannerRegistrarNotFoundConfigurationless

代码解读
复制代码
@Configuration @Import({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."); } }

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

很显然这个内部类是一个自动配置了,并且还使用了@Import注解导入了:AutoConfiguredMapperScannerRegistrar这个类,这个类就是自动扫描的核心类,下面进入这个类:

需要注意的是要想使用@Import生效,那么必须要满足上面的条件注解:Spring容器中没有:MapperFactoryBean和MapperScannerConfigurer

进入AutoConfiguredMapperScannerRegistrar这个类:这里我就截取部分源代码:csharp

代码解读
复制代码
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; private Environment environment; 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);

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

显然这个类实现了:ImportBeanDefinitionRegistrar这个类,那么自然需要重写:registerBeanDefinitions()方法,自然核心逻辑就在这个方法中:

在源码中能够清楚的发现它在构建一个beanDefinition:而构建的类就是:MapperScannerConfigurerini

代码解读
复制代码
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

并且还对这个beanDefinition添加了一些属性:vbscript

代码解读
复制代码
builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

其中比较关键的就是添加了需要扫描的注解为:Mapper注解。

需要扫描的路径为:Springboot扫描bean的路径, 这里并不是直接写死的,而是通过一个工具类获取到的,而这个包路径则是在启动的时候在@SpringBootApplication这个注解的@EnableAutoConfiguration注解的@AutoConfigurationPackage注解中添加进去的。

并且它导入的MapperScannerConfigurer这个类它实现了:BeanDefinitionRegistryPostProcessor这个beanFactoryPostProcessor,那么自然就要重写postProcessBeanDefinitionRegistry()方法,而扫描的逻辑就是在这个方法中实现的,而这个方法的调用就是在Spring启动的Refresh()方法中的:invokeBeanFactoryPostProcessors(beanFactory);方法中。

到这里Springboot整合mybatis实现Mapper接口的自动扫描源码就分析结束了,下面我们看看加了@MapperScan注解的情况。

3.加了@MapperScan注解:

如果项目中加了@MapperScan注解,那么程序员自然就会去配置扫描的路径,而Springboot就会直接去扫描配置的这个路径:

下面我们直接进入@MapperScan()注解:less

代码解读
复制代码
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { }

Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码

发现它也导入了:MapperScannerRegistrar这个类,那么其实现的逻辑跟上面的没有加@MapperScan注解的实现逻辑就是一样的了,只是扫描的包不一样而已。

4.总结:

在项目中使用MapperScan注解和不使用MapperScan注解其实区别不大,只是如果你不适应MapperScan注解的话,那么你需要对你的Mapper接口加上@Mapper注解那么Springboot才能够扫描到,如果你使用了@MapperScan注解的话,那么你只需要配置包路径即可。

注意:如果在项目中如果使用了@MapperScan注解的话,那么Springboot自动帮你扫描的那段逻辑也就是标题的第二点就不会生效了,因为我们看了它的源码,它那个内部类中有一个条件注解,其条件就是容器中不存在:MapperScannerRegistrar的话才会生效,显然当使用@MapperScan的时候也导入了MapperScannerRegistrar所以说容器中已经存在了,那么自动扫描那一套就自然不会生效,从而就不会导致重复扫描的问题了。

到这里不知道有没有一个疑问,就是:如果Springboot在启动的时候,先执行了Srpingboot的自动扫描,然后再执行了@MapperScan注解,这样的话也会导致扫描了两遍。其实不会的,它这里还是有一个优先级的, @MapperScan注解的优先级是大于Springboot的自动扫描。

转载来源:https://juejin.cn/post/7373306258404229129

Apipost 私有化火热进行中

评论