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

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware   

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

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

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  
  2.   if (this.processPropertyPlaceHolders) {  
  3.     processPropertyPlaceHolders();  
  4.   }  
  5.   
  6.   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);  
  7.   scanner.setAddToConfig(this.addToConfig);  
  8.   scanner.setAnnotationClass(this.annotationClass);  
  9.   scanner.setMarkerInterface(this.markerInterface);  
  10.   scanner.setSqlSessionFactory(this.sqlSessionFactory);  
  11.   scanner.setSqlSessionTemplate(this.sqlSessionTemplate);  
  12.   scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);  
  13.   scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);  
  14.   scanner.setResourceLoader(this.applicationContext);  
  15.   scanner.setBeanNameGenerator(this.nameGenerator);  
  16.   scanner.registerFilters();  
  17.   scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));  
  18. }  

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

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public int scan(String... basePackages) {  
  2.     int beanCountAtScanStart = this.registry.getBeanDefinitionCount();  
  3.   
  4.     doScan(basePackages);  
  5.   
  6.     // Register annotation config processors, if necessary.  
  7.     if (this.includeAnnotationConfig) {  
  8.         AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);  
  9.     }  
  10.   
  11.     return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;  
  12. }  

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner  

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


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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public Set<BeanDefinitionHolder> doScan(String... basePackages) {  
  3.   Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);  
  4.   
  5.   if (beanDefinitions.isEmpty()) {  
  6.     logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");  
  7.   } else {  
  8.     for (BeanDefinitionHolder holder : beanDefinitions) {  
  9.       GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();  
  10.   
  11.       if (logger.isDebugEnabled()) {  
  12.         logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()   
  13.             + "' and '" + definition.getBeanClassName() + "' mapperInterface");  
  14.       }  
  15.   
  16.       // the mapper interface is the original class of the bean  
  17.       // but, the actual class of the bean is MapperFactoryBean  
  18.       definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());  
  19.       definition.setBeanClass(MapperFactoryBean.class);  
  20.   
  21.       definition.getPropertyValues().add("addToConfig"this.addToConfig);  
  22.   
  23.       boolean explicitFactoryUsed = false;  
  24.       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {  
  25.         definition.getPropertyValues().add("sqlSessionFactory"new RuntimeBeanReference(this.sqlSessionFactoryBeanName));  
  26.         explicitFactoryUsed = true;  
  27.       } else if (this.sqlSessionFactory != null) {  
  28.         definition.getPropertyValues().add("sqlSessionFactory"this.sqlSessionFactory);  
  29.         explicitFactoryUsed = true;  
  30.       }  
  31.   
  32.       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {  
  33.         if (explicitFactoryUsed) {  
  34.           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");  
  35.         }  
  36.         definition.getPropertyValues().add("sqlSessionTemplate"new RuntimeBeanReference(this.sqlSessionTemplateBeanName));  
  37.         explicitFactoryUsed = true;  
  38.       } else if (this.sqlSessionTemplate != null) {  
  39.         if (explicitFactoryUsed) {  
  40.           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");  
  41.         }  
  42.         definition.getPropertyValues().add("sqlSessionTemplate"this.sqlSessionTemplate);  
  43.         explicitFactoryUsed = true;  
  44.       }  
  45.   
  46.       if (!explicitFactoryUsed) {  
  47.         if (logger.isDebugEnabled()) {  
  48.           logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");  
  49.         }  
  50.         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);  
  51.       }  
  52.     }  
  53.   }  
  54.   
  55.   return beanDefinitions;  
  56. }  

第一步就能看到,调用了父类的doScan(...)方法,父类的doScan(..)这里就不放出来了,它是Spring自动扫描机制的核心,也就是我们注册:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package="com.test "/>  

这个东西会用到的,它可以帮我们自动注册@Controller、@Service等组件。

本质上,也是通过扫描这些类,并将它们加入到BeanDefinition中,所以上面通过父类的doScan(...)方法其实是将我们设置的base-package下的类都封装成了BeanDefinition,并且添加到了一个Set集合中去了。也就是doScan(..)第一句得到的beanDefinitions。

但和父类略有不同的是,得到了所有的BeanDefinition之后,这并不真正要注入到容器中的类,还记得MyBatis手动把Mapper注入到Spring时是如何配置的吗:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">  
  2.   <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />  
  3.   <property name="sqlSessionFactory" ref="sqlSessionFactory" />  
  4. </bean>  

而上面通过自动扫描注册的Mapper的BeanDefinition是类似这样的:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="userMapper" class="com.test.a.mapper.UserMapper">  
  2. </bean>  

关键是这个class,所以需要转换,并将属性注入进去,mapperInterface和sqlSessionFactory都是必须的。

这样就完成了Mapper的自动扫描,不知道这样讲是否思路清晰~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值