ソースコード解析マッパーは、MyBatisのMapperProxyが初期化オブジェクトのスキャンおよび建設(図示)

@ [TOC](コード構築および走査対象MyBatisのMapperProxyマッパー初期の分析)

ヒント:この記事はMybatis.3.xバージョンをベースにしています。

MapperScannerConfigurer、その役割Maper MapperProxyオブジェクトのMyBatisのあるオブジェクトとして作成するには、プロジェクトのダオクラスをスキャンしている春の統合MyBatisのコアクラス、。

学習の最初の源に入る前に、私たちは、プロジェクト内のプロファイル情報を見てください。

ここに画像を挿入説明
私たちは、ここでは2つのまたは3つの関連マッパーの設定に注意してください。

  1. SqlSessionFactory#mapperLocations、あなたは構成XMLファイルへのパスを指定します。
  2. SqlSessionFactory#configLocation、指定mybaitsプロファイルは、プロファイルはまた、経路情報mapper.xmlを設定するように構成することができます。
  3. マッパーをスキャンMapperScannerConfigurer、Javaクラス(DAO)。

次のように言葉遣いのアイデアは、次のとおりです。

  1. MyBatisのMapperProxyの建設を持つオブジェクトをスキャンします
  2. どのようにこの部分に関連付けられマッパークラスのSQL文は、主にJavaクラスのインスタンスを実行して詳述マッパーとmapper.xml(接触UserMapper.xml、BookMapper.xmlファイルを確立する)方法(例えばUserMapper、BookMapper)オブジェクト。

MyBatisのMapperProxyオブジェクトの作成プロセス

以下、第一与えるシーケンス図MapperProxyを作成し、入力する前に、ソースコード解析、ソースコード解析ボーリングよりかもしれません。

1.1は、シーケンス図を作成MapperProxy

ここに画像を挿入説明

1.2 MapperScannerConfigurer詳細

MapperScannerConfigurerクラス図を以下に示します。

ここに画像を挿入説明
BeanNameAware、ApplicationContextAware、BeanFactoryPostProcessor、InitializingBean、BeanDefinitionRegistryPostProcessor、我々は初めて見に対応するこれらのインタフェースを呼び出す方法:クラスに関連付けられている春の豆のライフサイクルを達成MapperScannerConfigurer:

  • BeanNameAware BeanのBeanが自動的にビーンビーンの名前に配置された作成したとき、である自分自身の知覚、の名前で、外部アプリケーションを使用すると、getBeanName()メソッドによって、そのBean名を取得することができ、setBeanNameを呼び出す必要はありません。
  • 豆、春の植物が自動的に現在のApplicationContextのビーンに注入される時に作成されApplicationContextAware自動検知のApplicationContextオブジェクト。
  • インターフェイスを実装InitializingBean、春には、自動的に初期化ビーン後InitializingBean#afterPropertiesSetメソッドを呼び出します。
  • BeanFactoryPostProcessorたBeanFactoryポストプロセッサが、今回ビーン(BeanDefinition)の作成した定義情報、方法postProcessBeanFactory BeanFactoryPostProcessorインタフェースは、我々は、Beanスコープシングルトンを変更する、そのような属性の値を変更するように、ビーン定義情報を変更することができ以上の例。BeanPostProcessorそれに類似であり、これは、コンストラクタコール豆後、INIT-メソッド実行前に、即ち、前及び初期化後豆Beanで行われます。
  • BeanDefinitionRegistryPostProcessor主BeanDefinitionを高め、ビーンの定義を増大させるために使用。MapperScannerConfigurer主な目的は、特定のパッケージをスキャンすることで、対応するマッパーオブジェクトを作成しているので、ここでの焦点はMapperScannerConfigurerが実装され見積もります。

我々はBeanDefinitionRegistryPostProcessorの実装からインターフェイスを追跡を開始しようとしていること。

BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

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);     // @1
    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));    // @2
}
复制代码

1 @コード:スキャンマッパーから生成SqlSessionFactory最初のセットは、最終的にSqlSessionFactoryのそれに支配されます。コード@ 2:スキャンメソッド呼び出しClassPathMapperScannerスキャン操作が行われ、以下の詳細な説明。

ClassPathMapperScanner#doScan

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);   //@1
    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      processBeanDefinitions(beanDefinitions);   // @2
    }
    return beanDefinitions;
}
复制代码

1 @コード:最初のコール親クラス(org.springframework.context.annotation.ClassPathBeanDefinitionScanner)メソッドは、文書の走査に応じて、対応するBeanDefinitionHolderオブジェクトを構築しました。2 @コード:豆を処理BeanDefinitionsこれらの処理は、MyBatisの特性を追加しました。

ClassPathMapperScanner#processBeanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
      definition.setBeanClass(this.mapperFactoryBean.getClass());   // @1
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {    // @2 start
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }    // @2 end

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {  // @3
        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);
      }
    }
  }
复制代码

1コード@:この方法は、3つの重要な点を有したbeanClassクラスはBeanDefinitionザMapperFactoryBeanに設けられている、すなわち、インスタンスは、その名前が意味するBeanDefinitionザMapperFactoryBean、初期化され、これは方法のgetObjectによって構築されるFactoryBeanのオブジェクトであります具体的な例。

2コード@:SqlSessionFactoryがインスタンス化するときMapperFactoryBeanプロパティに設定され、そのプロパティへのSqlSessionFactoryを自動的に取得することができます。

コード@ 3:sqlSessionTemplateもし空でない場合、プロパティに入れて、インスタンス化MapperFactoryBeanで対応SqlSessionTemplate春のように得ることができます。

分析は、我々はMapperFactoryBeanになります、ここでは、doScan方法MapperScannerConfigurerのオーバー、しかし、マッパーを初期化していなかっただけでBeanDefinitionの多くを作成し、そのたbeanClassはMapperFactoryBeanです。

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

MapperFactoryBeanクラス図は次のとおりです。

ここに画像を挿入説明
まず、上記のコアクラスのスナップショットを作成します:

DaoSupport

DAO層ベースクラスは、サブクラスのために、テンプレートメソッドを定義する特定のロジックを実装する、DaoSupportテンプレート方法は次の通りであります:

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig(); // @1

	// Let concrete implementations initialize themselves.
	try {
		initDao();           // @2
	} catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}
复制代码

コード@ 1:チェックや構造DAOの構成情報は、この方法は、実現のための抽象クラス、サブクラスである、私たちはMyBatisの関連情報の統合を達成するために、メソッドを実装するMapperFactoryBean主であるこのセクションの次のリードを待ちます。@コード2:空の実装であるダオ関連する初期の方法。

SqlSessionDaoSupport

SQLSESSIONサポート親クラス、SqlSessionFactoryやSqlSessionTemplateを使用してSQLSESSIONを作成し、その後、次の2つの方法がときに呼び出されるのでしょうか?

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
复制代码

、そのビーンの例では、セットSqlSessionFacotryまたはSqlSessionTemplate、前掲コード(processBeanDefinitions)でそのプロパティのMapperFactoryBeanを作成するときに、覚えていれば知ってはいけない春自動的に注入インスタンス、そのBeanのインスタンスで上記方法では、一つ以上の場合、呼び出されます。

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

主にそれはのcheckDaoConfigをどのように実装されるかを確認します。

MapperFactoryBean#checkDaoConfig
protected void checkDaoConfig() {
    super.checkDaoConfig();   // @1

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

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

コード@ 1:まずcheckDaoConfig最初の親クラスのメソッドを呼び出します。2 @コード:mapperInterfaceは、com.demo.dao.UserMapperなどの特定のマッパーインターフェイスクラスは、登録された場合は、例外がスローされ、またはコールの設定がマッパーを増加します。

次に、中org.apache.ibatis.session.Configurationを入力します。

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
 }

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

public boolean hasMapper(Class<?> type) {
   return mapperRegistry.hasMapper(type);
}
复制代码

登録された上記のコードから分かるようMapperRegistryため、クエリ、アクセスマッパーコアクラス(追加)します。

1.4フォルダのレジストリ

コアクラス図を以下に示します。

ここに画像を挿入説明
そのプロパティは、簡単な紹介を行います。

  • コンフィギュレーション設定MyBatisのグローバル設定オブジェクト。
  • 地図は<クラス、MapperProxyFactory> knownMappersは地図を登録し、鍵はここにマッパーインタフェースである、などcom.demo.dao.UserMapperなど、MapperProxyFactoryは、MapperProxyファクトリを作成します。

以下は、いくつかの方法を達成することは比較的簡単であるMapperRegistryを、概説します。

フォルダのレジストリ#addMapper

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {   // @1
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));    // @2
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);    // @3
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
复制代码

コード@ 1:インターフェイスがすでに登録されている場合は、例外がすでにバインドスローされます。コード@ 2:登録MapperProxyFactoryのためのインタフェースが、ここでは単にMapperProxyを作成しないで、その作成のMapperProxy工場を登録します。コード@ 3:リソースマッパー対応するXMLがロードされていない場合は、XMLバインディング操作がトリガーされ、ビルド関係にマッパーを使用してXML SQLステートメント。この記事では、具体的に詳細に次のものを記載していません。

注:addMapper方法が、*マッパーに対応する対応MapperProxyFactoryを作成します。

MapperRegistry#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);   // @1
    if (mapperProxyFactory == null)
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      return mapperProxyFactory.newInstance(sqlSession);                                                                                 // @2
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
复制代码

マッパーインタフェースSQLSESSION基づいてオブジェクトを作成しますMapperProxy。1 @コード:GET MapperProxyFactoryインタフェース。2 @コード:のnewInstanceの呼び出しMapperProxyFactoryはMapperProxyオブジェクトを作成します。

初期構築プロセスは、これまでMyBatisのマッパーの半分はMapperScannerConfigurerそのパッケージをスキャンすることによって、行われ、その後、MapperProxyを構築するが、今回MapperProxyはまだmapper.xmlファイルのSQL文に関連付けられていない、スペースの理由は、次になりますセクションでは、彼らの関係を確立するプロセスに焦点を当てています。次に、我々はMapperProxyのプレビューは、オブジェクトを取得するには、すべての後に、これは、作成する究極の目標の記事ですが、また、フォローアップSQLの実装のために準備するための簡単なプロセスであることを。

1.5 MapperProxy

図クラス次のように:

ここに画像を挿入説明
上記タイプマッパーは、それぞれSQL文意志MapperMethodに対応する、SqlCommandオブジェクトから見ることができる方法の代表的な、MapperMethod比較的単純です。

以下SqlSessionFacotry各コアクラス斜視有する図です。

ここに画像を挿入説明

ヒント:この記事は唯一下記参照協会はこの部分の内容とは関係のない方法ですMyBatisのMapperProxy、MapperProxyここSQL * .Mapper.xml、次期リリースの作成プロセスを説明します。

著者は紹介:「RocketMQ技術インサイダー、」著者は、公共の番号維持:ミドルウェア興味円、主にJavaのソースコードの読み取りの収集、JUC(Javaの契約)、ネッティー、ElasticJob、Mycat、公表ダボ、RocketMQ、mybaits 他のソースを。

ここに画像を挿入説明

おすすめ

転載: juejin.im/post/5dcaa0fd51882557486c2de6