1. 程式人生 > >Mybatis 原始碼解析二、Mapper介面的代理實現過程 MapperScannerConfigurer 解析

Mybatis 原始碼解析二、Mapper介面的代理實現過程 MapperScannerConfigurer 解析

一、使用包掃描的配置方式生成Mapper的動態代理物件 上一篇文章中介紹了SqlSessionFactoryBean 通過Mybatis主配置檔案生成Mapper介面的代理物件,並將其儲存到Configuration中,但是一般在開發中我們並不是採用這種方式配置Mapper介面,因為這種方式是針對單個介面進行配置的,如果有大量的Mapper介面,這樣配置就很麻煩。開發中我們通常使用包掃描的方式進行配置。例如下面的配置:
  1. <!-- mybatis操作介面掃描-->
  2. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  3. <property name
    ="annotationClass" value="javax.annotation.Resource"/>
  4. <property name="basePackage" value="com.xxx.md.xxx.xxx.dao"/>
  5. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
  6. </bean>
   其中basePackage就是我們配置的Mapper檔案所在的包,我們追蹤下程式碼來看一下,MapperScannerConfigurer是如何通過包掃描的方式將我們的Mapper儲存到Configuration。

1.1 MapperScannerConfigurer 類     該類的主要作用是初始化我們配置的引數,並呼叫方法掃描配置的包。
    
  1. publicvoid postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throwsBeansException{
  2. if(this.processPropertyPlaceHolders){
  3. processPropertyPlaceHolders();
  4. }
  5. ClassPathMapperScanner scanner =newClassPathMapperScanner
    (registry);
  6. scanner.setAddToConfig(this.addToConfig);
  7. scanner.setAnnotationClass(this.annotationClass);
  8. scanner.setMarkerInterface(this.markerInterface);
  9. scanner.setSqlSessionFactory(this.sqlSessionFactory);
  10. scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  11. scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  12. scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  13. scanner.setResourceLoader(this.applicationContext);
  14. scanner.setBeanNameGenerator(this.nameGenerator);
  15. scanner.registerFilters();
  16. scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  17. }
    從上面程式碼我們可以看到真正執行掃描的是ClassPathMapperScanner類的Scan方法。
   1.2 ClassPathMapperScanner
        ClassPathMapperScanner類繼承了Spring提供的ClassPathBeanDefinitionScanner類。Scan方法的實現在父類中。
        
  1. publicint scan(String... basePackages){
  2. int beanCountAtScanStart =this.registry.getBeanDefinitionCount();
  3. doScan(basePackages);
  4. // Register annotation config processors, if necessary.
  5. if(this.includeAnnotationConfig){
  6. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  7. }
  8. return(this.registry.getBeanDefinitionCount()- beanCountAtScanStart);
  9. }
        在執行scan()方法時,會執行doScan()方法,這個方法在ClassPathMapperScanner類中有具體實現。
        
  1. publicSet<BeanDefinitionHolder> doScan(String... basePackages){
  2. Set<BeanDefinitionHolder> beanDefinitions =super.doScan(basePackages);
  3. if(beanDefinitions.isEmpty()){
  4. logger.warn("No MyBatis mapper was found in '"+Arrays.toString(basePackages)+"' package. Please check your configuration.");
  5. }else{
  6. for(BeanDefinitionHolder holder : beanDefinitions){
  7. GenericBeanDefinition definition =(GenericBeanDefinition) holder.getBeanDefinition();
  8. if(logger.isDebugEnabled()){
  9. logger.debug("Creating MapperFactoryBean with name '"+ holder.getBeanName()
  10. +"' and '"+ definition.getBeanClassName()+"' mapperInterface");
  11. }
  12. // the mapper interface is the original class of the bean
  13. // but, the actual class of the bean is MapperFactoryBean
  14. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
  15. definition.setBeanClass(MapperFactoryBean.class);
  16. definition.getPropertyValues().add("addToConfig",this.addToConfig);
  17. boolean explicitFactoryUsed =false;
  18. if(StringUtils.hasText(this.sqlSessionFactoryBeanName)){
  19. definition.getPropertyValues().add("sqlSessionFactory",newRuntimeBeanReference(this.sqlSessionFactoryBeanName));
  20. explicitFactoryUsed =true;
  21. }elseif(this.sqlSessionFactory !=null){
  22. definition.getPropertyValues().add("sqlSessionFactory",this.sqlSessionFactory);
  23. explicitFactoryUsed =true;
  24. }
  25. if(StringUtils.hasText(this.sqlSessionTemplateBeanName)){
  26. if(explicitFactoryUsed){
  27. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  28. }
  29. definition.getPropertyValues().add("sqlSessionTemplate",newRuntimeBeanReference(this.sqlSessionTemplateBeanName));
  30. explicitFactoryUsed =true;
  31. }elseif(this.sqlSessionTemplate !=null){
  32. if(explicitFactoryUsed){
  33. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  34. }
  35. definition.getPropertyValues().add("sqlSessionTemplate",this.sqlSessionTemplate);
  36. explicitFactoryUsed =true;
  37. }
  38. if(!explicitFactoryUsed){
  39. if(logger.isDebugEnabled()){
  40. logger.debug("Enabling autowire by type for MapperFactoryBean with name '"+ holder.getBeanName()+"'.");
  41. }
  42. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  43. }
  44. }
  45. }
  46. return beanDefinitions;
  47. }
        該方法首先會執行父類中的doScan()方法,父類中的doScan()方法也即Spring的包掃描,根據註解生成物件的方法,從原始碼中我們也可以看到在MapperScannerConfigure中配置的annotationClass是如何起到過濾作用的,如果不配置這個引數,會根據Spring中的預設註解生成物件,配置這個引數,我們可以指定我們自定義的註解。簡單來說Spring在生成物件的時候會有一個註解過濾器進行過濾,使用特定註解的類,才能由Spring生成物件,這也解釋了我們常用的註解起作用的過程。
        
  1. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
  2. definition.setBeanClass(MapperFactoryBean.class);
  3. definition.getPropertyValues().add("addToConfig",this.addToConfig);
      我們關鍵看一下這三行程式碼,很明顯由Spring包掃描的Mapper介面新增到了MapperFactoryBean中。  1.3  MapperFactoryBean類     MapperFactoryBean類實現了FactroyBean類,其中主要起作用的方法是cheakDaoConfig()。
  1. @Override