我们在使用springboot 整合MyBatis时,需要在启动类上添加上@MapperScan
注解,并写入mapper接口的包路径,然后我们就能通过从spring IOC容器中取对应的mapper的Bean来进行持久化操作了,那么@MapperScan
是如何将mapper接口实例化并注入到Spring IOC容器中的呢?
首先搭建一个spring boot项目,引入mybatis和mysql的相关maven包。在application.properties
中配置好连接参数。这些基操我就不写了
新建mapper,当然我这里取名取成dao了,意思到了就行。
package com.p6spy.demop6spy.dao;
import org.apache.ibatis.annotations.Select;
public interface TestDao {
@Select("select count(*) from subject_sae_detail")
String getInfo();
}
启动类上添加注解,指定扫描com.p6spy.demop6spy.dao
包
@SpringBootApplication
@MapperScan({
"com.p6spy.demop6spy.dao"})
public class Demop6spyApplication {
public static void main(String[] args) {
SpringApplication.run(Demop6spyApplication.class, args);
}
}
就能在service实现类中注入TestDao
来进行持久化操作了。
一.@MapperScan
首先让我们来看下@MapperScan
注解源码(这里为了清晰的了解扫描注入过程,所以我只选取了部分代码,其余的删除了)。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
String[] value() default {
};
String[] basePackages() default {
};
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
1.首先是@Import(MapperScannerRegistrar.class)
,从名字上看是一个扫描注册器,点击去看下,发现其继承于ImportBeanDefinitionRegistrar
。
主要作用是:
ImportBeanDefinitionRegistrar
接口的作用是当这个接口的实现类(类A)被@Import接口引入某个被标记了@Configuration的注册类(类B)时,可以得到这个类(类B)的所有注解,然后做一些动态注册Bean的事儿。
我们看下@MapperScan
,这也没有@Configuration
啊,别急,因为@MapperScan
是被启动类引用的,所以还要去看下启动类,虽然启动类看上去是只有@SpringBootApplication
和@MapperScan
两个注解,实际上是这样的。
@SpringBootApplication
如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@SpringBootConfiguration
如下
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
找到@Configuration
了。
2 . value
和basePackages
String[] value() default {
};
String[] basePackages() default {
};
这两行应该比较好辨认,就是需要我们填写的mapper接口所在的包路径信息。
就像这样写,后面我们会发现这俩填哪个都行,结果是一样的。
或者这样,这样写的话默认是赋值给value
的
3.annotationClass
,在这里的作用是:配置了该注解的mapper才会被扫描器扫描,与basePackage是与的作用。默认值是Annotation.class
。
Class<? extends Annotation> annotationClass() default Annotation.class;
4.markerInterface
,在这里的作用是:基于接口的过滤器,实现了该接口的mapper才会被扫描器扫描,与basePackage是与的作用。默认值是Class.class
。
Class<?> markerInterface() default Class.class;
5.构造工厂
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
二.MapperScannerRegistrar
在一中我们知道了实现ImportBeanDefinitionRegistrar
的大致作用。
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
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));
}
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