先上小结:
- @MapperScan注解生效。触发@Import({MapperScannerRegistrar.class})注解。
- @Import({MapperScannerRegistrar.class})注解生效。调用MapperScannerRegistrar.registerBeanDefinitions()。
- MapperScannerRegistrar创建扫描工具类Scanner。
- Scanner从@MapperScan注解中获取Mapper接口路径。
- 调用Scanner.doScan(),创建BeanDefinitionHolder,扫描接口路径中的Mapper接口,创建BeanDefinition,进而封装为BeanDefinitionHolder,向BeanFactory注册。
- BeanDefinition中的类名从原来的Mapper类名改为MapperFactoryBean.class。
- BeanDefinition创建完成。
正文:
当我们打开@MapperScan注解的代码,大概是这样的:
package org.mybatis.spring.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Import;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
首先,我们看到有个factoryBean()方法,默认返回的Class是MapperFactoryBean.class,全路径是org.mybatis.spring.mapper.MapperFactoryBean.class,这个class在后面定义BeanDefinition时会用到。
下面开始正题,注意到其中有个@Import注解:
@Import({MapperScannerRegistrar.class})
因为这个注解,使得被@MapperScan注解标识的类被Spring初始化时,其中的@Import注解会生效,效果是调用@Import中定义的类的registerBeanDefinitions()方法,在@MapperScan注解的@Import注解中,调用的是MapperScannerRegistrar类的registerBeanDefinitions()方法:
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);
}
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<String> 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));
}
其中创建了扫描工具类scanner,然后从@MapperScan注解中获取Mapper接口路径:
String[] var10 = annoAttrs.getStringArray("value");
得到的就是在@MapperScan注解中配置的Mapper接口路径,在上面的路径中,就是com.macro.mall.mapper和com.macro.mall.dao两个路径,最后把这两个路径传入scanner.doScan()方法。
scanner.doScan()方法代码:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> 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 {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
doScan()方法有两个关键点,第一个是调用父类ClassPathMapperScanner的同名方法:
super.doScan()
用于创建BeanDefinitionHolder,为生成BeanDefinition做准备。
另一个关键点是,调用本类的processBeanDefinitions()方法。
先看一下父类的doScan()方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length;
for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator();
while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
if (this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
可以看到,对于每一个路径来说,调用:
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
得到初步的Mapper接口的BeanDefinition,其中findCandidateComponents()方法的代码在父类ClassPathScanningCandidateComponentProvider中:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage);
}
此处componentsIndex是null,代码会调用后面的this.scanCandidateComponents()方法:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
LinkedHashSet candidates = new LinkedHashSet();
try {
String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = this.logger.isTraceEnabled();
boolean debugEnabled = this.logger.isDebugEnabled();
Resource[] var7 = resources;
int var8 = resources.length;
for(int var9 = 0; var9 < var8; ++var9) {
Resource resource = var7[var9];
if (traceEnabled) {
this.logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
if (this.isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
if (debugEnabled) {
this.logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
} else if (debugEnabled) {
this.logger.debug("Ignored because not a concrete top-level class: " + resource);
}
} else if (traceEnabled) {
this.logger.trace("Ignored because not matching any filter: " + resource);
}
} catch (Throwable var13) {
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13);
}
} else if (traceEnabled) {
this.logger.trace("Ignored because not readable: " + resource);
}
}
return candidates;
} catch (IOException var14) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14);
}
}
方法首先拼了一个文件路径:
String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
如果配置的路径是com.macro.mall,那么这里拼的路径就是:classpath*:com/macro/mall/**/*.class
然后按此路径加载Resource:
Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
这里的Resource数组就是Mapper接口类的数组了。
在后面的循环中,就是使用这些Resource来初始化BeanDefinition了,可以看到,BeanDefinition使用的具体实现类是ScannedGenericBeanDefinition类,也就是这段:
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
这个scanCandidateComponents()方法返回的就是一个ScannedGenericBeanDefinition的一个Set。
此方法完成后,回到ClassPathBeanDefinitionScanner的doScan()方法,this.findCandidateComponents()方法执行完成,我们得到了经过初始化的BeanDefinition列表,在后面的代码中,循环BeanDefinition并继续对其进行组装,其中创建了一下bean的名字:
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
这个名字是Mapper接口类的名字,默认只有类名没有路径,且为驼峰格式,比如com.macro.mall.mapper.CmsHelpCategoryMapper类得到的beanName就是cmsHelpCategoryMapper。
在循环的最后,创建了BeanDefinitionHolder,也就是这一段:
if (this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
BeanDefinitionHolder实际上只是对原来得到的BeanDefinition的一个简单封装,其中的属性有:
- private final BeanDefinition beanDefinition;
- private final String beanName;
- private final String[] aliases;
除了把刚才的BeanDefinition直接赋值进来之外,还赋值了beanName,aliases是别名,但是这里没用到。
后面调用this.registerBeanDefinition()方法把这个BeanDefinitionHolder往BeanFactory注册了一下,实际上就是维护了一个Map,key是beanName,value是BeanDefinitionHolder中的BeanDefinition。
当这个循环完成,我们得到了一个BeanDefinitionHolder的Set,至此ClassPathMapperScanner的doScan方法第一步,super.doScan()方法执行完成,再贴一下这段代码:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> 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 {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
下面进行第二步,执行this.processBeanDefinitions()方法,开始对BeanDefinition列表进行处理。
首先,贴一下this.processBeanDefinitions()方法代码:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
Iterator var3 = beanDefinitions.iterator();
while(var3.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(2);
}
}
}
很直接,上来就开始循环,从BeanDefinitionHolder中拿到之前初始化好的BeanDefinition,首先这一行:
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
把BeanClassName作为了将来用于构造的参数。
然后这一行:
definition.setBeanClass(this.mapperFactoryBean.getClass());
注意,这行代码把BeanDefinition的beanClass给改了,原来的beanClass是Mapper本身那个interface,这里改成了mapperFactoryBean,也就是最开始@MapperScan注解里的MapperFactoryBean.class,这是个工厂类,将来生成Mapper接口代理工厂的时候会用到。
后面的sqlSessionFactoryBeanName和sqlSessionTemplateBeanName,如果配置了相关的参数,也会加到BeanDefinition的参数列表里。
至此,Mapper接口相关的BeanDefinition初始化完成,后面Spring进行依赖注入和生成代理的时候,会用到上面的BeanDefinition。
(本文结束)