1. 概要

以前のチュートリアルでは、Springコンポーネントスキャンの基本について学習しました。

この記事では、@ComponentScanアノテーションで利用できるさまざまなタイプのフィルターオプションを紹介します。

2. @ ComponentScanフィルター

デフォルトでは、 @ Component、@ Repository、@ Service、@Controllerで注釈が付けられたクラスはSpringBeansとして登録されます。 @Componentでアノテーションが付けられたカスタムアノテーションでアノテーションが付けられたクラスについても同じことが言えます。 この動作は、@ComponentScanアノテーションのincludeFiltersおよびexcludeFiltersパラメーターを使用して拡張できます。

ComponentScan.Filterで使用できるフィルターには5つのタイプがあります。

  • 注釈
  • ASSIGNABLE_TYPE
  • ASPECTJ
  • 正規表現
  • 習慣

これらについては、次のセクションで詳しく説明します。

これらのフィルターはすべて、クラスをスキャンに含めたり、スキャンから除外したりできることに注意してください。 例を簡単にするために、クラスのみを含めます。

3. FilterType.ANNOTATION

ANNOTATION フィルタータイプは、特定の注釈でマークされたコンポーネントスキャンのクラスを含めるか除外します。

たとえば、 @Animalアノテーションがあるとします。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Animal { }

それでは、@Animalを使用するElephantクラスを定義しましょう。

@Animal
public class Elephant { }

最後に、 FilterType.ANNOTATION を使用して、Springに@Animalアノテーション付きクラスをスキャンするように指示しましょう。

@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
        classes = Animal.class))
public class ComponentScanAnnotationFilterApp { }

ご覧のとおり、スキャナーはElephantを正常に検出します。

@Test
public void whenAnnotationFilterIsUsed_thenComponentScanShouldRegisterBeanAnnotatedWithAnimalAnootation() {
    ApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(ComponentScanAnnotationFilterApp.class);
    List<String> beans = Arrays.stream(applicationContext.getBeanDefinitionNames())
            .filter(bean -> !bean.contains("org.springframework")
                    && !bean.contains("componentScanAnnotationFilterApp"))
            .collect(Collectors.toList());
    assertThat(beans.size(), equalTo(1));
    assertThat(beans.get(0), equalTo("elephant"));
}

4.FilterType。ASSIGNABLE_TYPE

ASSIGNABLE_TYPE は、コンポーネントスキャン中に、クラスを拡張するか、指定されたタイプのインターフェイスを実装するすべてのクラスをフィルタリングします。

まず、Animalインターフェースを宣言しましょう。

public interface Animal { }

また、 Elephant クラスを宣言しましょう。今回は、 Animal interface を実装します。

public class Elephant implements Animal { }

Animal:も実装しているCatクラスを宣言しましょう。

public class Cat implements Animal { }

それでは、 ASSIGNABLE_TYPE を使用してSpringをガイドし、Animalをスキャンしてみましょう-クラスを実装します。

@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
        classes = Animal.class))
public class ComponentScanAssignableTypeFilterApp { }

そして、CatElephantの両方がスキャンされることがわかります。

@Test
public void whenAssignableTypeFilterIsUsed_thenComponentScanShouldRegisterBean() {
    ApplicationContext applicationContext =
      new AnnotationConfigApplicationContext(ComponentScanAssignableTypeFilterApp.class);
    List<String> beans = Arrays.stream(applicationContext.getBeanDefinitionNames())
      .filter(bean -> !bean.contains("org.springframework")
        && !bean.contains("componentScanAssignableTypeFilterApp"))
      .collect(Collectors.toList());
    assertThat(beans.size(), equalTo(2));
    assertThat(beans.contains("cat"), equalTo(true));
    assertThat(beans.contains("elephant"), equalTo(true));
}

5. FilterType.REGEX

REGEX フィルターは、クラス名が特定の正規表現パターンに一致するかどうかを確認します。 FilterType.REGEX は、単純なクラス名と完全修飾クラス名の両方をチェックします。

もう一度、Elephantクラスを宣言しましょう。 今回は、インターフェイスを実装していないか、注釈が付けられていません

public class Elephant { }

もう1つのクラスCatを宣言しましょう。

public class Cat { }

それでは、Loinクラスを宣言しましょう。

public class Loin { }

使ってみよう FilterType 正規表現 Springに正規表現に一致するクラスをスキャンするように指示します 。*[nt]。 私たちの正規表現は、を含むすべてを評価します nt:

@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.REGEX,
        pattern = ".*[nt]"))
public class ComponentScanRegexFilterApp { }

今回のテストでは、Springが Elephant をスキャンしますが、 Lion はスキャンしません。

@Test
public void whenRegexFilterIsUsed_thenComponentScanShouldRegisterBeanMatchingRegex() {
    ApplicationContext applicationContext =
      new AnnotationConfigApplicationContext(ComponentScanRegexFilterApp.class);
    List<String> beans = Arrays.stream(applicationContext.getBeanDefinitionNames())
      .filter(bean -> !bean.contains("org.springframework")
        && !bean.contains("componentScanRegexFilterApp"))
      .collect(Collectors.toList());
    assertThat(beans.size(), equalTo(1));
    assertThat(beans.contains("elephant"), equalTo(true));
}

6. FilterType.ASPECTJ

式を使用してクラスの複雑なサブセットを選択する場合は、 FilterType ASPECTJを使用する必要があります。

このユースケースでは、前のセクションと同じ3つのクラスを再利用できます。

FilterType.ASPECTJ を使用して、SpringにAspectJ式に一致するクラスをスキャンするように指示しましょう。

@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASPECTJ,
  pattern = "com.baeldung.componentscan.filter.aspectj.* "
  + "&& !(com.baeldung.componentscan.filter.aspectj.L* "
  + "|| com.baeldung.componentscan.filter.aspectj.C*)"))
public class ComponentScanAspectJFilterApp { }

少し複雑ですが、ここでのロジックでは、クラス名に「L」も「C」も含まないBeanが必要であるため、Elephantが再び残ります。

@Test
public void whenAspectJFilterIsUsed_thenComponentScanShouldRegisterBeanMatchingAspectJCreteria() {
    ApplicationContext applicationContext =
      new AnnotationConfigApplicationContext(ComponentScanAspectJFilterApp.class);
    List<String> beans = Arrays.stream(applicationContext.getBeanDefinitionNames())
      .filter(bean -> !bean.contains("org.springframework")
        && !bean.contains("componentScanAspectJFilterApp"))
      .collect(Collectors.toList());
    assertThat(beans.size(), equalTo(1));
    assertThat(beans.get(0), equalTo("elephant"));
}

7. FilterType.CUSTOM

上記のフィルタータイプのいずれも要件を満たしていない場合は、 カスタムフィルタータイプを作成することもできます。 たとえば、名前が5文字以下のクラスのみをスキャンするとします。

カスタムフィルターを作成するには、org.springframework.core.type.filter.TypeFilterを実装する必要があります。

public class ComponentScanCustomFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader,
      MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String fullyQualifiedName = classMetadata.getClassName();
        String className = fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf(".") + 1);
        return className.length() > 5 ? true : false;
    }
}

FilterTypeCUSTOMを使用して、カスタムフィルター ComponentScanCustomFilter:を使用してSpringをスキャンクラスに伝達します。

@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,
  classes = ComponentScanCustomFilter.class))
public class ComponentScanCustomFilterApp { }

次に、カスタムフィルター ComponentScanCustomFilter:のテストケースを見てみましょう。

@Test
public void whenCustomFilterIsUsed_thenComponentScanShouldRegisterBeanMatchingCustomFilter() {
    ApplicationContext applicationContext =
      new AnnotationConfigApplicationContext(ComponentScanCustomFilterApp.class);
    List<String> beans = Arrays.stream(applicationContext.getBeanDefinitionNames())
      .filter(bean -> !bean.contains("org.springframework")
        && !bean.contains("componentScanCustomFilterApp")
        && !bean.contains("componentScanCustomFilter"))
      .collect(Collectors.toList());
    assertThat(beans.size(), equalTo(1));
    assertThat(beans.get(0), equalTo("elephant"));
}

8. 概要

このチュートリアルでは、@ComponentScan。に関連付けられたフィルターを紹介しました

いつものように、完全なコードはGitHubから入手できます。