前面我们说到在创建SqlSessionFactory
的时候会一并把mapper文件解析,但是如果使用@Mapper注解类会单独使用AutoConfigUredMapperScannerRegistrar
内部类来扫描。
@org.springframework.context.annotation.Configuration
//把当前类导入配置类中,并且把当前类的对象注入到容器中
@Import({ AutoConfiguredMapperScannerRegistrar.class })
//仅当容器中不存在MapperFactoryBean实例不存在的时候才创建@Import导入的类的实例
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration {
//servlet容器初始化的时候执行一次
@PostConstruct
public void afterPropertiesSet() {
logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
//存在MyBatisAutoConfigUration中的内部类,这个类只会在启动类没有贴上@MapperScan的时候才会执行
/**
* 这个类实现了三个接口
* BeanFactoryAware:通过调用这个接口,实现里面的方法可以获取存放这个
*
*/
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("Searching for mappers annotated with @Mapper");
//这个类是扫描@Mapper注解的关键类
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
//得到@autoConfiguration所在类的包,我这里是com.miaowenzhao.blogs
List 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);
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
- 首先通过
@Import
将AutoConfigUredMapperScannerRegistrar
注入到容器并立即调用实现方法registerBeanDefinitions()
,这个方法内部创建了一个ClassPathMapperScanner
对象进行@Mapper
类的扫描 - 另外向容器注入
MapperFactoryBean
对象,这个专门用于将扫描到的类创建代理对象,并注入到容器中。 -
scanner.registerFilters();
具体看这个方法public void registerFilters() { //当他为true表示要扫描所有接口 boolean acceptAllInterfaces = true; // 这里判断我们有没有传入注解类,也就是上面代码传入的Mapper.class if (this.annotationClass != null) { //这是父类的方法,跟表示只扫描Mapper.class注解所标注的类 addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); //只要找到一个就设置为false 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; } }); }
如果我用@MapperScan
呢?
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) //这里导入这个类,这个类是关键
public @interface MapperScan {}
MapperScannerRegistrar.class
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//通过这个方法可以把注解中的所有封装成AnnotationAttributes extend LinkedHashMap
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//这里仍然是这个类进行扫包
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//...中间都是一些赋值操作
//这里会设置进MapperFactoryBean,并且会注入到容器中
Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
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));
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
- 这里我们真正体会到其实具体的扫包、注入功能都是在
ClassPathMapperScanner
类中实现 - 设置了
@MapperScan
后将会忽略@Mapper
进入scanner.doScan(StringUtils.toStringArray(packages));
方法
@Override
public Set doScan(String... basePackages) {
//通过调用父类的doScan方法传入参数成功返回一个set集合,这个集合就保存贴有@Mapper注解的类路径
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
//调用这个方法进行具体的处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
- 通过调用
super.doScan(basePackages);
扫描出映射类,并存放到Set
集合中
进入processBeanDefinitions(beanDefinitions);
private void processBeanDefinitions(Set beanDefinitions) {
GenericBeanDefinition definition;
//这里for循环,配置每个Mapper类的属性并设置进GenericBeanDefinition
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
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) {
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) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
通过源码我们可以总结出结论
如果设置了
@MapperScan
将会忽略@Mapper
,只会扫描@MapperScan
中用户指定的包路径具体的扫包、注入功能都是在
ClassPathMapperScanner
类中实现,这是MyBatis关键类@Import({ AutoConfiguredMapperScannerRegistrar.class })
直接将类注入到容器中,这个类可以实现ImportBeanDefinitionRegistrar
,重写方法后会在方法中获取BeanDefinitionRegistry
,可以自定义注册。-
@ConditionalOnMissingBean(MapperFactoryBean.class)
按条件注入,贴在容器上时,表示当容器中存在当前类的实例则容器中的Bean都不会注入到容器中。当首先创建MapperScannerRegistrar
的时候就向容器中注入了MapperFactoryBean
,Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); }
则下面的
@Import
无法注入@org.springframework.context.annotation.Configuration //把当前类导入配置类中,并且把当前类的对象注入到容器中 @Import({ AutoConfiguredMapperScannerRegistrar.class }) //仅当容器中不存在MapperFactoryBean实例不存在的时候才创建@Import导入的类的实例 @ConditionalOnMissingBean(MapperFactoryBean.class) public static class MapperScannerRegistrarNotFoundConfiguration { @PostConstruct public void afterPropertiesSet() { logger.debug("No {} found.", MapperFactoryBean.class.getName()); } }
MyBatis注解版分析(1).
MyBatis注解版分析(3).