1. 概述
在之前的教程中,我們學習了 Spring 組件掃描的基礎知識。
在這篇文檔中,我們將瞭解帶有 <em><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html">@ComponentScan</a> </em> 標註的不同類型的過濾器選項。
2. @ComponentScan 過濾器
默認情況下,帶有 <em/>@Component</em/>、<em/>@Repository</em/>、<em/>@Service</em/>、<em/>@Controller</em/> 註解的類會被註冊為 Spring Bean。同樣,帶有自定義註解,且該自定義註解被註解為@Component</em/> 的類也會遵循此規則。我們可以通過使用 <em/>@ComponentScan</em/> 註解的includeFilters</em/> 和 `excludeFilters</em/> 參數來擴展此行為。
`ComponentScan.Filter</em/> 提供了五種類型的過濾器:
- ANNOTATION</em/> (註解)
- ASSIGNABLE_TYPE</em/> (可賦值類型)
- ASPECTJ</em/> (AspectJ)
- REGEX</em/> (正則表達式)
- CUSTOM</em/> (自定義)
我們將會在下一部分中詳細介紹這些過濾器。
請注意,所有這些過濾器都可以包含或排除掃描的類。為了簡化示例,我們只會在示例中包含類。
3.FilterType.ANNOTATION
“ANNOTATION” 過濾器類型包括或排除在給定註解標記的組件掃描中的類。
例如,我們有一個 @Anima 標註:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Animal { }現在,讓我們定義一個 Elephant 類,它使用 @Animal 屬性:
@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. 過濾器類型ASSIGNABLE_TYPE
過濾器 ASSIGNABLE_TYPE 會在組件掃描期間過濾所有擴展指定類型類或實現接口的類。
首先,聲明Animal接口:
public interface Animal { }再次聲明我們的 Elephant 類,這次實現 Animal 接口:
public class Elephant implements Animal { }讓我們聲明我們的 Cat 類,該類同時實現 Animal 接口:
public class Cat implements Animal { }現在,我們使用 ASSIGNABLE_TYPE 來引導 Spring 掃描 Animal 實現類的:
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
classes = Animal.class))
public class ComponentScanAssignableTypeFilterApp { }我們將會看到 貓 和 大象 都會被掃描。
@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
The REGEXfilter checks if the class name matching a given regex pattern. FilterType.REGEX checks both simple and fully-qualified class names.
Once again, let’s declare our Elephant class. This time not implementing any interface or annotated with any annotation:
public class Elephant { }讓我們聲明一個額外的類 Cat:
public class Cat { }現在,讓我們聲明 Lion 類:
public class Lion { }讓我們使用 <em>FilterType</em>.<em>REGEX</em>,它指示 Spring 掃描與正則表達式 <em>.*[nt]. </em> 匹配的類。我們的正則表達式表達式會評估所有包含 <em>nt:</em> 的內容。
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.REGEX,
pattern = ".*[nt]"))
public class ComponentScanRegexFilterApp { }這次測試中,我們將看到 Spring 會掃描 Elephant,但不會掃描 Lion: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。
對於這個用例,我們可以重用前一節中相同的三個類。
讓我們使用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”開頭的豆子,這使得我們再次得到 象類:
@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
如果以上任何過濾類型均不符合我們的要求,我們還可以創建自定義過濾類型。例如,假設我們只想掃描名稱不超過五個字符的類。
要創建自定義過濾,我們需要實現 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;
}
}讓我們使用 FilterType.CUSTOM,它指示 Spring 使用我們的自定義過濾器 ComponentScanCustomFilter 來掃描類:
@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 相關的過濾器。