Spring中@Importの各種用法及びImportAwareインターフェースの詳細解


@Importコメント
@ImportコメントはXMLの「import/」と等価の機能を提供し、導入された1つまたは複数の構成クラスを実現します。Importはカテゴリでも使用できますし、メタ注釈としても使用できます。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

  /**
   * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
   * or regular component classes to import.
   */
  Class<?>[] value();

}

注中は一つだけです。@Configrationの表示を導入する構成類をサポートし、Import Selectorインターフェースを実現する類、ImportBenDefinitionRegistrarインターフェースを実現する類と普通の@component類。
メタ注釈として使用する
@Importは元の注釈として使用できます。@Importの引き継ぎの上に一つの層をカプセル化することができます。私の理解では、このようにして外部に私の内部の具体的な実現の細部を暴露することはできません。
例を挙げます。例えば@EnbaleAspect JAtoxy注。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
@EnbleAsppectJAtoProxyは@Importという元の注釈によってマークされています。私たちは@EnbaleAsppectJAtoxyを使ってAsppectJAtoProxyをオープンします。Springの下の階は@Importを導入して対応する配置類を実現します。
Import Selectorインターフェースを実現するクラスを導入します。
まずImport Selectorインターフェースを見てみます。このインターフェースには一つの方法しかありません。

public interface ImportSelector {
  String[] selectImports(AnnotationMetadata importingClassMetadata);
}
Import Selectorは、セレクタを入力します。インターフェースは、与えられた条件に基づいて、どのような構成クラスを導入するかを選択するために使用されます。
例えば@EnbaleTransation Managementコメント。

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
@EnbaleTransaction Managementコメントには@Import(Transact Management ConfigrationSelector.class)の注釈が使われていますが、Transation Management Configration Selector類はImport Selectorインターフェースを実現しました。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
  @Override
  protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
      case PROXY:
        return new String[] {AutoProxyRegistrar.class.getName(),
            ProxyTransactionManagementConfiguration.class.getName()};
      case ASPECTJ:
        return new String[] {
            TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
      default:
        return null;
    }
  }
}
方法の内部実装ロジックも簡単で、Adviice Modeによって異なる配置類を導入し、事務管理を実現します。
ImportBeanDefinitionRegistarインターフェースを実現するクラスを導入します。
ImportBeanDefinitionRegistarインターフェースにも一つの方法しかありません。

public interface ImportBeanDefinitionRegistrar {
  void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
このインターフェースは、与えられた注釈メタデータに基づいて、必要に応じて追加のBeanDefinitionを登録することを許可します。
例を挙げます。例えば@EnbaleAspect JAtoxy注。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
@EnbaleAspect JAtoxy注はAsppectJAxyRegistar.class類を導入しました。この類はImportBeanDefinitionRegistarインターフェースを実現しました。
class Aspect JAutoxyRegistar implemens ImportBeanDefinitionRegistar{

  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
    }
  }
}

register BeanDefinitionsでAopConfigUtils.register ApectJAnnotation AutoxyCreatoIf Necessaryを呼び出しました。方法として、この方法は伝来したBeanDefinitionRegistry registryにBeanDefinitionを登録することです。BenDefinitionを登録すると、SpringはこのBeaビンを実際化してAsppectJAtoxyとして機能します。
導入@Configrationクラス
今回は@Importが一番よく使われています。構成クラスを分割して、必要に応じてプログラムに対応する構成を導入することができます。
例を挙げます。例えば@EnbaleRetryコメントです。この注釈を使ってretry機能を開くことができます。

@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
public @interface EnableRetry {
内部には、RetryConfigrationという設定類が導入されています。
ImportAwareインターフェース
ImportAwareインターフェースは@Importと一緒に使う必要があります。@Importを元の注釈として使用する場合、@Importで導入されたプロファイルがImport Awareインターフェースを実現すれば、そのプロファイルクラスインターフェースに導入されたデータ配置を取得することができます。回りくどいです。直接コードを入れます。
例を挙げます。

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
//AsyncConfigurationSelector  
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

  private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
      "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
  @Override
  @Nullable
  public String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
      case PROXY:
        return new String[] {ProxyAsyncConfiguration.class.getName()};
      case ASPECTJ:
        return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
      default:
        return null;
    }
  }
}

デフォルトではAdvice ModeをPROXYとして使用し、ProxyAcConfigrationクラスを導入しました。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

  @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
    Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
    AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
    Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
    if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
      bpp.setAsyncAnnotationType(customAsyncAnnotation);
    }
    if (this.executor != null) {
      bpp.setExecutor(this.executor);
    }
    if (this.exceptionHandler != null) {
      bpp.setExceptionHandler(this.exceptionHandler);
    }
    bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
    bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
    return bpp;
  }
}

ProxyAcConfigrationのasync Advisor方法では、@EnbaleAsync上のいくつかの設定値を取得する必要があります。例えば、this.enable Aync.get Boolean、this.enaber Aync.getNumber.(「order」)。
this.enableAsyncは、その親タイプのAbstract Aync Configrationの属性です。AbstractAcyncConfigrationはImportAwareインターフェースを実現し、@EnbaleAync上の情報を得ることができます。

// AbstractAsyncConfiguration#setImportMetadata   
public void setImportMetadata(AnnotationMetadata importMetadata) {
  this.enableAsync = AnnotationAttributes.fromMap(
      importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
  if (this.enableAsync == null) {
    throw new IllegalArgumentException(
        "@EnableAsync is not present on importing class " + importMetadata.getClassName());
  }
}
この例はちょっと複雑かもしれません。もう一つの簡単な例があります。これについて、読者は自分でソースコードのdebugを見て勉強してもいいです。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。