首先我们来看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);
-
-
- 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<BeanDefinitionHolder> doScan(String... basePackages) {
- Set<BeanDefinitionHolder> 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");
- }
-
-
-
- definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
- definition.setBeanClass(MapperFactoryBean.class);
-
- 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);
- }
- }
- }
-
- return beanDefinitions;
- }
第一步就能看到,调用了父类的doScan(...)方法,父类的doScan(..)这里就不放出来了,它是Spring自动扫描机制的核心,也就是我们注册:
- <context:component-scan base-package="com.test "/>
这个东西会用到的,它可以帮我们自动注册@Controller、@Service等组件。
本质上,也是通过扫描这些类,并将它们加入到BeanDefinition中,所以上面通过父类的doScan(...)方法其实是将我们设置的base-package下的类都封装成了BeanDefinition,并且添加到了一个Set集合中去了。也就是doScan(..)第一句得到的beanDefinitions。
但和父类略有不同的是,得到了所有的BeanDefinition之后,这并不真正要注入到容器中的类,还记得MyBatis手动把Mapper注入到Spring时是如何配置的吗:
- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
- <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
- <property name="sqlSessionFactory" ref="sqlSessionFactory" />
- </bean>
而上面通过自动扫描注册的Mapper的BeanDefinition是类似这样的:
- <bean id="userMapper" class="com.test.a.mapper.UserMapper">
- </bean>
关键是这个class,所以需要转换,并将属性注入进去,mapperInterface和sqlSessionFactory都是必须的。
这样就完成了Mapper的自动扫描,不知道这样讲是否思路清晰~