<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Mybatis的Mapper掃描機(jī)制:@MapperScan源碼(轉(zhuǎn))

      原文:https://blog.csdn.net/qq_42187215/article/details/132650985

      作者:Zzzj_1233

      來源:CSDN

      1. 如何基于包掃描類

      首先提出一個(gè)問題:如通過給定包名來掃描包中類的信息呢?

      方式一:使用Class.forName

      首先,通過包名訪問目錄下的所有class文件。
      接下來,將所有class文件的路徑轉(zhuǎn)換為類路徑格式,例如將 “a/b/c/HelloWorld.class” 轉(zhuǎn)換為 “a.b.c.HelloWorld”。
      最后,使用 Class.forName 方法加載 a.b.c.HelloWorld。
      方式二:基于字節(jié)碼庫

      同樣,首先通過包名訪問目錄下的所有class文件。
      然后,通過字節(jié)碼庫讀取這些class文件,以獲取類信息。
      Spring采取的是方式二

      并且使用ASM作為字節(jié)碼庫, ( ASM是java字節(jié)碼類庫中性能最高的之一 )

      那為什么Spring不采用Class.forName呢

      使用Class.forName必定會(huì)經(jīng)歷查找 -> 加載 -> 初始化的過程,最終將類加載到JVM中
      可是我們指定一個(gè)包去掃描,只需要掃描出包含特定注解的類,而并不需要將所有的類都加載到JVM中

      2. @MapperScan的用處

      在傳統(tǒng)的Mybatis開發(fā)中

      通過SqlSession#getMapper來得到Mapper
      然后使用Mapper進(jìn)行數(shù)據(jù)庫訪問
      而在Mybatis集成Spring的開發(fā)中,只需要給配置類上標(biāo)注@MappperScan("指定包名")即可將Mapper注入到Spring的容器中

      例如:

      @SpringBootApplication
      @MapperScan("com.zzzj.dao")
      public class Main {
          public static void main(String[] args) {
              SpringApplication.run(Main.class, args)
          }
      }

      2. 概要

      @MapperScan和@ComponentScan一樣,都是基于ClassPathBeanDefinitionScanner實(shí)現(xiàn)的

      只不過@MapperScan做了一些額外的處理,將掃描出來的BeanDefinition替換了,來了一手貍貓換太子

      如果還不熟悉ClassPathBeanDefinitionScanner

      可以查看這篇文章:Spring的Bean掃描機(jī)制:ClassPathBeanDefinitionScanner源碼

      3. 源碼追蹤

      3.1 @Import(MapperScannerRegistrar.class)

      @MapperScan注解上使用@Import注解導(dǎo)入了MapperScannerRegistrar

      @Import(MapperScannerRegistrar.class)
      public @interface MapperScan { 
          // ...
      }

      MapperScannerRegistrar向Spring容器中導(dǎo)入了MapperScannerConfigurer

      3.2 MapperScannerConfigurer

      MapperScannerConfigurer實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口,將在BeanDefinitionRegistry被Spring創(chuàng)建好后被回調(diào)

      回調(diào)postProcessBeanDefinitionRegistry方法

      此時(shí)的流程如下圖所示

       

      3.3 postProcessBeanDefinitionRegistry

      接下來跟蹤MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法

        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            // 忽略
          if (this.processPropertyPlaceHolders) {
            processPropertyPlaceHolders();
          }
          
          // 1. 創(chuàng)建scanner, { ClassPathMapperScanner } 繼承了 { ClassPathBeanDefinitionScanner }
      
          // { this.$propertyName }  來自@MapperScan注解的屬性
       
          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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
          if (StringUtils.hasText(lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
          }
          if (StringUtils.hasText(defaultScope)) {
            scanner.setDefaultScope(defaultScope);
          }
          scanner.registerFilters();
          
          // 調(diào)用scan方法, 掃描包下的類
          scanner.scan(
              StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
        }

      3.4 scan

      上面有提到,ClassPathMapperScanner繼承自ClassPathBeanDefinitionScanner

      ClassPathMapperScanner并沒有重寫scan方法,這里還是調(diào)用的ClassPathBeanDefinitionScanner的scan方法

      Spring的Bean掃描機(jī)制:ClassPathBeanDefinitionScanner源碼

      在上篇文章中已經(jīng)追蹤過scan方法的具體流程了,就不再贅述

      但是

      ClassPathMapperScanner重寫了ClassPathBeanDefinitionScanner的doScan方法

      接下來繼續(xù)跟追doScan方法

          //  ClassPathBeanDefinitionScanner 的 scan 方法源碼
          
          public int scan(String... basePackages) {
              int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
      
              doScan(basePackages);
              
              if (this.includeAnnotationConfig) {
                  AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
              }
      
              return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
          }

      3.5 doScan

      注意這里是ClassPathMapperScanner重寫的doScan方法

        @Override
        public Set<BeanDefinitionHolder> doScan(String... basePackages) {
            
            // 1. 得到super ( ClassPathBeanDefinitionScanner ) 掃描后的 beanDefinition 集合
          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 {
              
              // 2. 對(duì) beanDefinition 集合做額外的處理
            processBeanDefinitions(beanDefinitions);
          }
      
          return beanDefinitions;
        }

      為什么ClassPathMapperScanner要對(duì)掃描出來的BeanDefinition做額外的處理?

      日常開發(fā)中,基本都是定義一個(gè)接口,作為Mapper

      例如

      public interface UserMapper {
          User selectById(int id);
      }

      UserMapper被掃描出來后,ClassPathBeanDefinitionScanner為其創(chuàng)建對(duì)應(yīng)的BeanDefinition對(duì)象

      BeanDefinition對(duì)象的BeanClass就是UserMapper.class

      而一個(gè)接口是無法直接被Spring實(shí)例化的,我們需要的是SqlSession.getMapper創(chuàng)建出來的代理對(duì)象

      ClassPathMapperScanner的額外處理就是修改BeanDefinition的屬性,使其最終還是通過SqlSession.getMapper方法創(chuàng)建Mapper對(duì)象

      ====================================================================================================

      那么接下來繼續(xù)追蹤processBeanDefinitions方法,看看這個(gè)方法究竟做了什么額外處理

      3.6 processBeanDefinitions

        private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        
          AbstractBeanDefinition definition;
          
          for (BeanDefinitionHolder holder : beanDefinitions) {
          
            definition = (AbstractBeanDefinition) holder.getBeanDefinition();
      
            String beanClassName = definition.getBeanClassName();
      
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
              
            // 注意這里: 修改了BeanClass
            definition.setBeanClass(this.mapperFactoryBeanClass);
      
            definition.getPropertyValues().add("addToConfig", this.addToConfig);
      
            definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);
      
            // ... 忽略邊緣邏輯代碼
      
          }
        }

      在該方法中, 最最核心的一行代碼就是

      definition.setBeanClass(this.mapperFactoryBeanClass);

      mapperFactoryBeanClass屬性在ClassPathMapperScanner被定義

      public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
          // ....
          
          private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
          
          // ....
      }

      也就是說,ClassPathMapperScanner 將掃描出的BeanDefinition的BeanClass替換為了MapperFactoryBean

       

      3.7 MapperFactoryBean

      MapperFactoryBean是一個(gè)FactoryBean

      我們關(guān)注該類的getObject方法

        @Override
        public T getObject() throws Exception {
          return getSqlSession().getMapper(this.mapperInterface);
        }

      可以看到,兜兜轉(zhuǎn)轉(zhuǎn),最終還是通過SqlSession.getMapper方法來創(chuàng)建Mapper的

      那么該對(duì)象的SqlSessionmapperInterface是怎么來的呢?

      還是在processBeanDefinitions方法中被賦值的

      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
      
          AbstractBeanDefinition definition;
      
          for (BeanDefinitionHolder holder : beanDefinitions) {
            
            definition = (AbstractBeanDefinition) holder.getBeanDefinition();
            
            String beanClassName = definition.getBeanClassName();
      
            // 1. 通過 { MapperFactoryBean } 的構(gòu)造方法注入 { mapperInterface } 屬性
          
            // beanClassName也就是被掃描的接口全限定名, 例如 "com.zzzj.UserMapper"
            
            // 注意: MapperFactoryBean 的構(gòu)造方法接收一個(gè)Class對(duì)象, 而不是String對(duì)象 ( beanClassName是String類型的 )
            // 在Spring調(diào)用 { MapperFactoryBean } 構(gòu)造方法前會(huì)進(jìn)行值的轉(zhuǎn)換
            // 將String轉(zhuǎn)換為Class
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            
            definition.setBeanClass(this.mapperFactoryBeanClass);
          
             // 2. 這里賦值的 { sqlSessionTemplate }
            if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
              definition.getPropertyValues().add("sqlSessionTemplate",
                  new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
              explicitFactoryUsed = true;
            } else if (this.sqlSessionTemplate != null) {
              definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
              explicitFactoryUsed = true;
            }
              
            // ......
      
          }
        }

      4. 總結(jié)

      1. @MapperScan通過@Import注解導(dǎo)入了MapperScannerRegistrar
      2. MapperScannerRegistrar向容器中注入MapperScannerConfigurer
      3. MapperScannerConfigurer是一個(gè)BeanDefinitionRegistryPostProcessor,將會(huì)被Spring容器回調(diào)postProcessBeanDefinitionRegistry方法
      4. 在postProcessBeanDefinitionRegistry方法中創(chuàng)建了ClassPathMapperScanner,并且調(diào)用ClassPathMapperScanner的scan方法
      5. ClassPathMapperScanner重寫了父類ClassPathBeanDefinitionScanner的doScan方法,在掃描完Bean獲取到BeanDefinition后,在processBeanDefinitions方法對(duì)其進(jìn)行了額外的處理
      6. processBeanDefinitions方法將所有的BeanDefinition的beanClass屬性轉(zhuǎn)換為了MapperFactoryBean
      7. MapperFactoryBean是一個(gè)FactoryBean,將會(huì)被Spring回調(diào)getObject()方法,并且將方法的返回值注入到容器中
      8. MapperFactoryBean的getObject()方法通過sqlSession#getMapper方法創(chuàng)建Mapper對(duì)象,注入到容器中

       

      posted @ 2025-06-25 17:52  奮斗終生  Views(170)  Comments(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 人妻护士在线波多野结衣 | 国产亚洲精品久久yy50| 野花社区在线观看视频| 亚洲sm另类一区二区三区| 熟女在线视频一区二区三区| 99人体免费视频| 国产精品亚洲二区在线播放| 久久久久久综合网天天| 精品久久久久久无码专区不卡 | 亚洲综合在线一区二区三区| 亚洲av日韩av综合在线观看| 久久精品国产免费观看频道| 国产精品久久毛片| 精品国产午夜理论片不卡| 亚洲精品日产AⅤ| 亚洲国产aⅴ成人精品无吗| 国产精品播放一区二区三区| 中国CHINA体内裑精亚洲日本| 精品精品亚洲高清a毛片| 五月综合激情婷婷六月| 熟女蜜臀av麻豆一区二区| 妇女自拍偷自拍亚洲精品| 日本一区不卡高清更新二区| 男女18禁啪啪无遮挡激烈网站| 亚欧洲乱码视频一二三区| 合江县| 国产亚洲一在无在线观看| 国产精品大片中文字幕| 熟女精品视频一区二区三区| 国产国产久热这里只有精品| 色窝窝免费播放视频在线| 特黄少妇60分钟在线观看播放| 亚洲国产成人精品区综合| 色吊丝av熟女中文字幕| 思思久99久女女精品| 亚洲综合91社区精品福利| 在线国产极品尤物你懂的| 猫咪AV成人永久网站在线观看 | 亚洲日韩AV秘 无码一区二区 | 久久se精品一区精品二区国产 | 色欲AV无码一区二区人妻|