@MapperScan注解是spring和mybatis整合的关键注解,这个注解的作用也简单,就是为了把当前mapper接口,放到spring容器中,但是这篇笔记想要记录的是,@MapperScan注解在1.3版本和2.0版本中的区别; 这个注解是放在mybatis-spring这个jar包中
在1.3版本中,通过@Import注解引入了一个bean:
MapperScannerRegistrar
这里可以看到,这个bean是ImportBeanDefinitionRegistrar的实现类,spring源码:@Import注解 在这篇博客中有介绍,这个接口的作用,简单来说,我们实现这个接口之后,可以拿到BeanDefinitionRegistry对象,可以自己去声明一个beanDefinition对象,放到beanDefinitionMap中,然后spring容器会帮我们完成初始化的操作
我们接着来看实现类中的实现方法
@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 basePackages = new ArrayList();
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));
}
可以看到,这里直接初始化了一个scanner对象,然后根据当前要扫描的包,去扫描mapper接口,doScan方法中就和spring扫描@Component注解的代码是类似的,这篇博客中不展开详细叙述,我们只需要知道,这里会帮我们把所有的mapper接口扫描出来,然后根据扫描结果,初始化beanDefinition对象
在2.X版本中,虽然@MapperScan注解也是注入了ImportBeanDefinitionRegistry的实现类,但是在实现方法中,逻辑不同,可以看到,在2.X版本中,并没有初始化scanner,然后去扫码,而是继续注册了一个bean:
MapperScannerConfigurer
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
我们接着来看MapperScannerConfigurer这个bean的作用
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
首先我们可以看到,MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的实现类,
关于这个接口的作用,在前面博客中有介绍过,我们直接来看实现类的实现方法中做了什么
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
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.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
可以发现,这里是真正去扫描mapper接口的地方,扫描之后,然后注册为beanDefinition对象
所以,最后再总结一下,不管是哪个版本,@MapperScan注解都是注入了一个beanDefinitionRegistry的实现类,不同的是:
所以,在2.X版本中,额外利用到了BeanDefinitionRegistryPostProcessor的扩展机制