缘起:之前写了一篇xml的配置源码分析mybatis与spring的整合之MapperFactoryBean,本篇讲解注解式自动扫描的源码分析
启用注解@MapperScan
只需要这么一个配置类,就完成了MyBatis-Spring
的整合,具体看@MapperScan
注解
@Configuration
@ComponentScan("cn.yukang.javaConfig")// 扫描包
@PropertySource("classpath:datasource.properties")// 加载数据源配置信息
@MapperScan("cn.yukang.javaConfig.mapper")
public class MyConfig {
@Value("${jdbc.driverClassName}")
private String driverclassname;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String passWord;
@Bean
public BasicDataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverclassname);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(passWord);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 数据库与javaBean映射关系
//sessionFactory.setTypeAliasesPackage("cn.yukang.javaConfig.bean");
// 添加映射-使用接口+xml的形式(xml的命名空间为接口)
//sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper.xml"));
return sessionFactory;
}
}
通过@Import
导入了MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
public @interface MapperScan
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions
方法,可以详看另一篇文章处理
带有ImportBeanDefinitionRegistrar的类
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
// 处理@MapperScan注解中的属性
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((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
}
Class extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List basePackages = new ArrayList();
String[] var10 = annoAttrs.getStringArray("value");
int var11 = var10.length;
int var12;
String pkg;
for(var12 = 0; var12 < var11; ++var12) {
pkg = var10[var12];
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
var10 = annoAttrs.getStringArray("basePackages");
var11 = var10.length;
for(var12 = 0; var12 < var11; ++var12) {
pkg = var10[var12];
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
var11 = var14.length;
for(var12 = 0; var12 < var11; ++var12) {
Class> clazz = var14[var12];
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
上面方法内容就是获取@MapperScan
注解中的属性值, 比如@MapperScan("cn.yukang.javaConfig.mapper")
就是获取value
属性,放入集合basePackages
中,然后调用scanner.doScan(StringUtils.toStringArray(basePackages));
开始扫描注册
public Set doScan(String... basePackages) {
// 调用父类ClassPathBeanDefinitionScanner.doScan
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
// 处理注册后的bd
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
这里先调用父类ClassPathBeanDefinitionScanner.doScan
方法扫描包路径并注册bd。可详看另一篇文章spring加载流程之ClassPathBeanDefinitionScanner
循环处理bd
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
beanClass
为MapperFactoryBean.class
definition.setBeanClass(this.mapperFactoryBean.getClass());
addToConfig
为true
definition.getPropertyValues().add("addToConfig", this.addToConfig);
如果没有显示
的指定SqlSessionFactoryBean,则设置当前bd的属性注入方式为按类型注入
,与在属性上加了@Autowired
效果一致
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
// 设置属性按类型注入
definition.setAutowireMode(2);
}
因为没有手动注入SqlSessionFactoryBean给MapperFactoryBean,所以在spring实例化bd注入属性的时候,会按类型注入
的方式找到类型为SqlSessionFactoryBean的进行注入
生成代理对象就由MapperFactoryBean去完成了mybatis与spring的整合之MapperFactoryBean