春MyBatisの、ソースコードを統合する方法は難しいものではありません!

Spring統合Mybtaisは、(すべての道路は、ローマにいない唯一の方法を導く)は次のように構成されます。

private static final String ONE_MAPPER_BASE_PACKAGE = "com.XXX.dao.mapper.one";
@Bean
public MapperScannerConfigurer oneMapperScannerConfigurer() {
    MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    mapperScannerConfigurer.setBasePackage(ONE_MAPPER_BASE_PACKAGE);
    mapperScannerConfigurer.
                setSqlSessionFactoryBeanName("oneSqlSessionFactoryBean");
    return mapperScannerConfigurer;
}
@Primary
@Bean(name="oneSqlSessionFactoryBean")
public SqlSessionFactoryBean oneSqlSessionFactoryBean( @Qualifier("oneDataSource") DruidDataSource oneDataSource) {
    return getSqlSessionFactoryBeanDruid(oneDataSource,ONE_MAPPER_XML);
}
复制代码

コード未満の20行では、春MyBatisの統合を完了します。

素晴らしいです!最後に後ろに何が起こったのか?

私たちは、MapperScannerConfigurerとSqlSessionFactoryBeanから進めるべき。

MapperScannerConfigurer

クラスノート

  • beanDefinitionRegistryPostProcessorベースパッケージ再帰検索インターフェースから、彼らはMapperFactoryBeanとして登録されます。注インタフェースは、少なくともメソッドの実装クラスは無視されます含まれている必要があります。

  • BeanFactoryPostProcessor前1.0.1は1.0.2がBeanDefinitionRegistryPostProcessorを拡張することがされた後、https://jira.springsource.org/browse/SPR-8269特定の理由を参照してください、延長され

  • basePackageは、複数の設定カンマやセミコロンを使用して分割することができます。

  • またはannotationClass markerInterfaceにより、スキャンインタフェースを提供することができる指定します。2つの属性が、basePackage下のすべてのインターフェイスは、デフォルトでは、空スキャンされます。

  • MapperScannerConfigurer Beanは、それが複数ある場合は自動注射又はSqlSessionFactory SqlSessionFactory SqlSessionTemplateを作成し、またはsqlSessionFactoryBeanName sqlSessionTemplateBeanNameが注入特定又はsqlSessionFactory sqlSessionTemplateを指定設定することです。

  • (:ユーザー名とパスワードのプレースホルダーが含まれているオブジェクトデータベースなど)のオブジェクトのプレースホルダが渡すことはできません。あなたがのbeanNameを使用することができ、実際のオブジェクトを持つすべてのプレースホルダの完了後まで延期作成します。MapperScannerConfigurerのフォーマットを使用して、プレースホルダ、$ {プロパティ}を持つ独自の特性をサポートします。

クラス図を見つけるための鍵方式

MapperScanConfigurer
MapperScanConfigurer

クラス図からMapperScannerConfigurerはBeanDefinitionRegistryPostProcessor、InitializingBean、ApplicationContextAware、BeanNameAwareインターフェイスを実現しました。次のように各インタフェースは説明しました:

  • ApplicationContextAware:初期スプリングコンテナの後、自動的にApplicationContextの注射
  • BeanNameAware:春の豆で現在の名前を設定します。
  • InitializingBeanインターフェイスのみafterPropertiesSet方法が実行される含むとき豆の初期化
  • BeanDefinitionRegistryPostProcessor:BeanFactoryPostProcessor拡張BeanFactoryPostProcessorの実行前に、複数の登録Beanの定義を可能とします。私たちは、postProcessBeanDefinitionRegistryための方法を拡張する必要があります。

クエリは、afterPropertiesSetのMapperScannerConfigurerには、特定の拡張情報を、以下ません。

@Override public void afterPropertiesSet() throws Exception {
notNull(this.basePackage, "Property 'basePackage' is required"); 
}
复制代码

コアを決定するためにMapperScannerConfigurerクラス図及び解析方法を結合注:postProcessBeanDefinitionRegistry

postProcessBeanDefinitionRegistry分析

@Override
public void postProcessBeanDefinitionRegistry(
                    BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
      //1. 占位符属性处理
    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);
  //2.设置过滤器
  scanner.registerFilters();
  //3.扫描java文件
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, 
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
复制代码

processPropertyPlaceHoldersに加えて、ソースから見て、他の作業委託ClassPathMapperScanner

プレースホルダを処理processPropertyPlaceHolders

BeanFactoryPostProcessorはBeanDefinitionRegistryPostProcessorは、実行前に呼び出された前に、

これは、春のプレースホルダークラスPropertyResourceConfigurer処理が実行されていないことを意味します!

どのように独自のプロパティをMapperScannerConfigurerサポートは、プレースホルダを使用していますか?すべての答えは、

processPropertyPlaceHoldersこの方法。

private void processPropertyPlaceHolders() {
  Map<String, PropertyResourceConfigurer> prcs =
       applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
  if (!prcs.isEmpty() && applicationContext 
                      instanceof GenericApplicationContext) {
    BeanDefinition mapperScannerBean = 
            ((GenericApplicationContext) applicationContext)
                        .getBeanFactory().getBeanDefinition(beanName);
    // PropertyResourceConfigurer 没有暴露方法直接替换占位符,
    // 创建一个 BeanFactory包含MapperScannerConfigurer
    // 然后执行BeanFactory后处理即可
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition(beanName, mapperScannerBean);

    for (PropertyResourceConfigurer prc : prcs.values()) {
      prc.postProcessBeanFactory(factory);
    }
    PropertyValues values = mapperScannerBean.getPropertyValues();
    this.basePackage = updatePropertyValue("basePackage", values);
    this.sqlSessionFactoryBeanName =
            updatePropertyValue("sqlSessionFactoryBeanName", values);
    this.sqlSessionTemplateBeanName = 
            updatePropertyValue("sqlSessionTemplateBeanName", values);
  }
}
复制代码

processPropertyPlaceHoldersを読んだ後、まとめることができるプレースホルダの方法を使用して独自のプロパティをサポートしていMapperScannerConfigurer

  1. 豆のすべての登録PropertyResourceConfigurerタイプを探します

  2. 新しいDefaultListableBeanFactory()春のシミュレーション環境を使用し、これはたBeanFactory MapperScannerConfigurerに登録され、たBeanFactoryは、プレースホルダを置き換えるために、後処理を行いました。

registerFiltersのClassPathMapperScanner方法

持ってMapperScannerConfigurer注釈の種類:

またはannotationClass markerInterfaceによって、スキャン・インタフェースを提供することができる、デフォルトの属性2が空である、basePackage下のすべてのインターフェイスをスキャンするように指定。scanner.registerFilters()は、annotationClassとのmarkerInterfaceに設定されています。

public void registerFilters() {
  boolean acceptAllInterfaces = true;

  // 如果指定了annotationClass,
  if (this.annotationClass != null) {
    addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
    acceptAllInterfaces = false;
  }
  // 重写AssignableTypeFilter以忽略实际标记接口上的匹配项
  if (this.markerInterface != null) {
    addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
      @Override
      protected boolean matchClassName(String className) {
        return false;
      }
    });
    acceptAllInterfaces = false;
  }

  if (acceptAllInterfaces) {
    // 默认处理所有接口
    addIncludeFilter(new TypeFilter() {
      @Override
      public boolean match(
      MetadataReader metadataReader, 
      MetadataReaderFactory metadataReaderFactory) throws IOException {
        return true;
      }
    });
  }

  // 不包含以package-info结尾的java文件
  // package-info.java包级文档和包级别注释
  addExcludeFilter(new TypeFilter() {
    @Override
    public boolean match(MetadataReader metadataReader, 
    MetadataReaderFactory metadataReaderFactory) throws IOException {
      String className = metadataReader.getClassMetadata().getClassName();
      return className.endsWith("package-info");
    }
  });
}
复制代码

フィルタセットが、それはスキャン方式scanner.scanどのように動作するかに依存します。

スキャン方式ClassPathMapperScanner

public int scan(String... basePackages) {
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
   doScan(basePackages);
   // 注册注解配置处理器
   if (this.includeAnnotationConfig) {
      AnnotationConfigUtils
                  .registerAnnotationConfigProcessors(this.registry);
   }
   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
复制代码

doScanは次のとおりです。

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 {
    processBeanDefinitions(beanDefinitions);
  }
  return beanDefinitions;
}
复制代码

doScan法ClassPathBeanDefinitionScanner位置ClassPathMapperScannerの親クラス、すなわち、

JavaファイルはBeanDefinition(実際ScannedGenericBeanDefinition)にすべてのパケットをスキャンします。

processBeanDefinitionsはBeanDefinitionのMapperFactoryBeanにBeanDefinition前に変換することです。

力(すなわちannotationClassまたはmarkerInterface)それにフィルタリングする方法については?私はすべての方法のソースを追跡しました

最後にisCandidateComponentのClassPathScanningCandidateComponentProviderにフィルタのハンドルを見つけました

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, this.metadataReaderFactory)) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, this.metadataReaderFactory)) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}
复制代码

アクションの概要MapperScannerConfigurer

MapperScannerConfigurerはpostProcessBeanDefinitionRegistryのbeanDefinitionRegistryPostProcessor方法を実現しました

指定されたディレクトリから再帰的にBasePackageインタフェースを検索し、彼らはMapperFactoryBeanとして登録されます

SqlSessionFactoryBean

クラスノート

  1. Springコンテキストを共有するためSqiSessionFactory MyBatisの作成。

  2. SqiSessionFactory DAOS従属スルーMyBatisの中に注入されてもよいです。

  3. datasourcetransactionmanager、jtatransactionmanagerはsqlsessionfactory実装トランザクションと組み合わせたいです。

クラス図を見つけるための鍵方式

sqlSessionFactoryBean
sqlSessionFactoryBean类图

次のようにSqlSessionFactoryBeanはApplicationListener、InitializingBean、FactoryBeanのインターフェース、説明した各インターフェイスを実現しました。

  • 春のためのApplicationListenerイベントリスナー
  • InitializingBeanインターフェイスのみafterPropertiesSet方法が実行される含むとき豆の初期化
  • FactoryBeanの:FactoryBeanのによって返されるオブジェクトgetObjectメソッドを返す、クラスの返されたオブジェクトのインスタンスを指定しません

私たちは、afterPropertiesSetとgetObjectメソッドを集中すべきです。

分析の主要な方法

afterPropertiesSet方法

public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, 
              "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) 
          || !(configuration != null && configLocation != null),
  "Property 'configuration' and 'configLocation' can not specified with together");
  this.sqlSessionFactory = buildSqlSessionFactory();
}
复制代码

特定のソースのSqlSessionFactoryを作成するため、ここで知っているメソッド名を参照してくださいbuildSqlSessionFactoryはここでは繰り返しません。

getObjectメソッド

public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }
  return this.sqlSessionFactory;
}
复制代码

概要SqlSessionFactoryBean

SqlSessionFactoryのMyBatisのを作成するにはafterPropertiesSetの実現InitializingBean、

FactoryBeanの達成のgetObject戻りは良いsqlSessionFactoryを作成します。

疑い

ご質問があれば、このSqlSessionFactoryBeanとMapperScannerConfigurerを読んだ後、私は知りません!一般的に、次の方法の春に使用MyBatisの:

ApplicationContext context=new AnnotationConfigApplicationContext();
UsrMapper  usrMapper=context.getBean("usrMapper");
实际上调用的是
sqlSession.getMapper(UsrMapper.class);
复制代码

SqlSessionFactoryBeanはSqlSessionFactoryのMyBatisのを作成しました。MapperFactoryBeanにMapperScannerConfigurerインタフェースコンバータ。彼らはそれ(UsrMapper.class)sqlSession.getMapperを呼び出すことはどこにありますか?

(:マッパー工場!! ---私の名前を見てMapperFactoryBean)MapperFactoryBeanは、このすべての答えです

説明MapperFactoryBean

クラスノート

これは、たBeanFactory MyBatisのマッピングインターフェイスを注入することができます。これは、SqlSessionFactory SqlSessionTemplateまたは事前に設定を提供することができます。
このインタフェースは、射出工場のみ実装クラスを注入しないことに注意してください

クラス図を見つけるための鍵方式

フォルダファクトリービーン
フォルダファクトリービーン

クラス図を参照してください、そしてInitializingBeanとFactoryBeanのを見ました!

  • InitializingBeanインターフェイスのみafterPropertiesSet方法が実行される含むとき豆の初期化
  • FactoryBeanの:FactoryBeanのによって返されるオブジェクトgetObjectメソッドを返す、クラスの返されたオブジェクトのインスタンスを指定しません

AfterPropertiesSet再びgetObjectメソッドに焦点を当て実現!

分析の主要な方法

次のようにDaoSupport afterPropertiesSetクラスが実装されています:

public final void afterPropertiesSet()
     throws IllegalArgumentException, BeanInitializationException {
    this.checkDaoConfig();
    try {
        this.initDao();
    } catch (Exception var2) {
        throw
         new BeanInitializationException(
                             "Initialization of DAO failed",  var2);
    }
}
复制代码

initDaoが空の実装で、MapperFactoryBeanを実現するためのcheckDaoConfigは次のとおりです。

protected void checkDaoConfig() {
  super.checkDaoConfig();

  notNull(this.mapperInterface, "Property 'mapperInterface' is required");

  Configuration configuration = getSqlSession().getConfiguration();
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
      configuration.addMapper(this.mapperInterface);
    } catch (Exception e) {
      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
      throw new IllegalArgumentException(e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}
复制代码

keyステートメントは、MyBatisの設定にインターフェイスを追加し、configuration.addMapper(this.mapperInterface)です。

超簡単なgetObjectメソッドは、それがsqlSession.getMapper(UsrMapper.class)と呼ばれています。

public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface); 
}
复制代码

概要MapperFactoryBean

AfterPropertiesSetはMyBatisの構成にマッパーインターフェースする、方法InitializingBean達成します。

FactoryBeanのはsqlSession.getMapperと呼ばれるgetObjectメソッド、戻りマッパーオブジェクトを実現しました。

概要

春の統合MyBatisのコア三つのカテゴリー:

MapperScannerConfigurer

BeanDefinitionRegistryPostProcessorが指定されたディレクトリから再帰検索インタフェースbasePackageたpostProcessBeanDefinitionRegistry方法を実現し、それらはBeanDefinitionのMapperFactoryBeanタイプとして登録されます

SqlSessionFactoryBean

afterPropertiesSetのInitializingBean、SqlSessionFactory MyBatisのの中に作成を実現。

FactoryBeanの達成のgetObject戻りは良いsqlSessionFactoryを作成します。

フォルダファクトリービーン

AfterPropertiesSetは、方法InitializingBeanマッパMyBatisの構成へのインタフェースを実現しました。

FactoryBeanのはsqlSession.getMapperと呼ばれるgetObjectメソッド、戻りマッパーオブジェクトを実現しました。

おすすめ

転載: juejin.im/post/5dbe9b0be51d452a066994e5