交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
mybatis第1 章:启动:Mapper接口扫描
分享
未结
0
1501
李延
LV6
2021-05-30
悬赏:20积分
# 内容 在中springboot使用mybatis会自动扫描Mapper接口,加载到spring中提供我们使用。那么springboot是怎么找到这些接口的,我们具体看一下。 # 示例 要让springboot字段加载Mapper接口有两种方式。 1. 在启动类上添加@MapperScan,指定具体的mapper所在的包名: ```java @MapperScan("com.test.mybatis.test.mapper") @SpringBootApplication public class ApplicationMain { public static void main(String[] args) { final ConfigurableApplicationContext run = SpringApplication.run(ApplicationMain.class, args); } } ``` 2. 在每个Mapper接口上添加 @Mapper 注解 ```java @Mapper public interface UserMapper { User sel(int id); @Select("Select * from user") List<User> findAll(); } ``` 我们分别来看一下这两个的加载过程。 ## MapperScan 点开MapperScan注解我们看到 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) public @interface MapperScan { } ``` 有一个Import注解。而里面的MapperScannerRegistrar是ImportBeanDefinitionRegistrar的子类。这个我们在springboot启动的时候分析过,在加载BeanDefinition时后调用其registerBeanDefinitions方法 ```java @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //获取到MapperScan的配置 AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); //创建ClassPathMapperScanner对象 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // this check is needed in Spring 3.1 if (resourceLoader != null) { scanner.setResourceLoader(resourceLoader); } Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); } //设置MapperFactoryBean,我们Mapper接口最后创建出来的对象就是这个对象。 Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); } scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); //设置扫描的路径 List<String> basePackages = new ArrayList<String>(); for (String pkg : annoAttrs.getStringArray("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : annoAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); } ``` 在这里我们看到了ClassPathMapperScanner对象,而它是ClassPathBeanDefinitionScanner的子类。这个类我们之前也分析过。 作用是扫描指定路径下的class文件,并根据Filters属性筛选出需要的bean定义。 我们根进registerFilters方法看一下,Filters就是在这里设置的。 ```java public void registerFilters() { boolean acceptAllInterfaces = true; // if specified, use the given annotation and / or marker interface if (this.annotationClass != null) { addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; } // override AssignableTypeFilter to ignore matches on the actual marker interface if (this.markerInterface != null) { addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { @Override protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; } if (acceptAllInterfaces) { // default include filter that accepts all classes addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); } // exclude package-info.java addExcludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); } }); } ``` 当以MapperScan注解方式加载时,没有设置其他的属性,所以会运行这段代码: ```java if (acceptAllInterfaces) { // default include filter that accepts all classes addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); } ``` 我们看到这里是直接返回true。也就是说MapperScan逐渐会加载指定路径下的所以class文件。 ## Mapper 在springboot项目中,我们之前分析知道会去自动加载spring.factories的org.springframework.boot.autoconfigure.EnableAutoConfiguration变量。 在mybatis的jar包内有这样的文件:  所以我们看一下MybatisAutoConfiguration类的内容 在这里我们看到通过@Bean加载了2个对象 - SqlSessionFactory - SqlSessionTemplate 这两个我们之后分析 我们主要看下面这个子类 ```java @org.springframework.context.annotation.Configuration @Import({ AutoConfiguredMapperScannerRegistrar.class }) @ConditionalOnMissingBean(MapperFactoryBean.class) public static class MapperScannerRegistrarNotFoundConfiguration { @PostConstruct public void afterPropertiesSet() { logger.debug("No {} found.", MapperFactoryBean.class.getName()); } } ``` 这里也是Import注解,但有一个条件: ```java @ConditionalOnMissingBean(MapperFactoryBean.class) ``` 也就是我在我们使用@MapperScan的时候,这部分代码是不会出发的。 我们看一下AutoConfiguredMapperScannerRegistrar类: ```java @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); try { if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { for (String pkg : packages) { logger.debug("Using auto-configuration base package '{}'", pkg); } } scanner.setAnnotationClass(Mapper.class); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(packages)); } catch (IllegalStateException ex) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex); } } ``` 我刚才我们分析的类扫描基本一致,都是使用ClassPathMapperScanner。 但不同同的是这里扫描的springboot设置的packake,刚才是我们自己指定的路径。并且这里设置了scanner.setAnnotationClass(Mapper.class);只扫描有@Mapper的注解。 # 总结 通过上面的分析我们知道mybatis是如何自动加载Mapper接口的。同时我们也看到:在初始化的时候还加载了 - SqlSessionFactory - SqlSessionTemplate 并且我们Mapper接口最后其实创建的是MapperFactoryBean类。 这3个类,将是我们下面的分析重点。
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号