从MapperScannerConfigurer看MyBatis自动扫描Mapper的机制(一)

2015-01-25 11:37:58 · 作者: · 浏览: 14

首先我们来看MapperScannerConfigurer的继承和实现关系

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware 

这里我们关注BeanDefinitionRegistryPostProcessor这个接口:它继承了BeanFactoryPostProcessor,用来在BeanFactoryPostProcessor初始化之前再往容器中注册一些额外的BeanDefinition。

看到这里你应该有点想法了吧?对,它就是扫描了我们给的basePackage下的类之后,然后生成BeanDefinition再添加到容器中的。

下面还是从源码入手,我们先看BeanDefinitionRegistryPostProcessor初始化时调用的方法:

  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    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));
  }

这里我们先看ClassPathMapperScanner这个类,可以从名字上(Scanner)看出来,它就是去Scan的核心类。并且除了最后一行,其余的代码都是在配置这个Scanner。

这里根据名字我们可以猜想,最后一行scanner.scan(...)就是在扫描Mapper并将它添加到BeanDefinition中去。为了验证我们的想法,跟进去看看:

	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
	}

发现进的并不是ClassPathMapperScanner这个类,而是ClassPathBeanDefinitionScanner这个类,并且ClassPathBeanDefinitionScanner在spring的包下。那么再回过头来看看ClassPathBeanDefinitionScanner的定义:

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

恩,确实是继承了org.springframework.context.annotation.ClassPathBeanDefinitionScanner,并且我们可以看到它覆盖了好几个方法:

\

doScan(...),熟吗?不熟的请往上看到scan方法中。监测的逻辑实际上都在doScan(...)中,接下来我们就关注这个Override的方法:

  @Override
  public Set
  
    doScan(String... basePackages) {
    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 { for (BeanDefinitionHolder holder : beanDefinitions) { GenericBeanDefinition 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.getPropertyValues().add("mapperInterface", definition.getBeanClassName