1. Overview
1.概述
In an earlier tutorial, we learned about the basics of Spring component scans.
在之前的教程中,我们了解了Spring组件扫描的基本原理。
In this write-up, we’ll see the different types of filter options available with the @ComponentScan annotation.
在这篇文章中,我们将看到不同类型的过滤器选项与@ComponentScanannotation.可用。
2. @ComponentScan Filter
2.@ComponentScanFilter
By default, classes annotated with @Component, @Repository, @Service, @Controller are registered as Spring beans. The same goes for classes annotated with a custom annotation that is annotated with @Component. We can extend this behavior by using includeFilters and excludeFilters parameters of the @ComponentScan annotation.
默认情况下,用@Component、@Repository、@Service、@Controller注解的类被注册为Spring Bean。用自定义注解的类也是如此,该注解是用@Component注释的。我们可以通过使用includeFilters和excludeFilters参数的@ComponentScan注解来扩展这种行为。
There are five types of filters available for ComponentScan.Filter :
对于ComponentScan.Filter,有五种类型的过滤器可用:
- ANNOTATION
- ASSIGNABLE_TYPE
- ASPECTJ
- REGEX
- CUSTOM
We’ll see these in detail in the next sections.
我们将在接下来的章节中详细介绍这些内容。
We should note that all these filters can include or exclude classes from scanning. For simplicity in our examples, we’ll only include classes.
我们应该注意,所有这些过滤器都可以包括或排除扫描的类。在我们的例子中,为了简单起见,我们将只包括类。
3. FilterType.ANNOTATION
3. FilterType.ANNOTATION
The ANNOTATION filter type includes or excludes classes in the component scans which are marked with given annotations.
ANNOTATION过滤器类型包括或排除组件扫描中标有特定注释的类。
Let’s say, for example, that we have an @Animal annotation:
比方说,我们有一个@Animal注释。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Animal { }
Now, let’s define an Elephant class which uses @Animal:
现在,让我们定义一个Elephant类,它使用@Animal。
@Animal
public class Elephant { }
Finally, let’s use the FilterType.ANNOTATION to tell Spring to scan for @Animal-annotated classes:
最后,让我们使用FilterType.ANNOTATION来告诉Spring扫描@Animal注释的类。
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = Animal.class))
public class ComponentScanAnnotationFilterApp { }
As we can see, the scanner picks up our Elephant just fine:
正如我们所看到的,扫描仪可以很好地捕捉到我们的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
4.FilterType.ASSIGNABLE_TYPE
The ASSIGNABLE_TYPE filters all classes during the component scan that either extend the class or implement the interface of the specified type.
ASSIGNABLE_TYPE在组件扫描过程中过滤所有扩展该类或实现指定类型接口的类。
First, let’s declare the Animal interface:
首先,让我们声明Animal接口。
public interface Animal { }
And again, let’s declare our Elephant class, this time implementing Animal interface:
再一次,让我们声明我们的Elephant类,这次实现了Animalinterface:。
public class Elephant implements Animal { }
Let’s declare our Cat class that also implementing Animal:
让我们声明我们的Cat类,它也实现了Animal:。
public class Cat implements Animal { }
Now, let’s use ASSIGNABLE_TYPE to guide Spring to scan for Animal-implementing classes:
现在,让我们用ASSIGNABLE_TYPE来指导Spring扫描Animal实现的类。
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
classes = Animal.class))
public class ComponentScanAssignableTypeFilterApp { }
And we’ll see that both Cat and Elephant get scanned:
而我们会看到,猫和大象都会被扫描到。
@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
5.FilterType.REGEX
The REGEX filter checks if the class name matching a given regex pattern. FilterType.REGEX checks both simple and fully-qualified class names.
REGEX过滤器检查类名是否与给定的regex模式相匹配。FilterType.REGEX同时检查简单的和完全限定的类名。
Once again, let’s declare our Elephant class. This time not implementing any interface or annotated with any annotation:
再一次,让我们声明我们的Elephant类。这一次没有实现任何接口,也没有使用任何注解:。
public class Elephant { }
Let’s declare one more class Cat:
让我们再声明一个类Cat。
public class Cat { }
Now, let’s declare Lion class:
现在,让我们声明Lion类。
public class Lion { }
Let’s use FilterType.REGEX which instructs Spring to scan classes that match regex .*[nt]. Our regex expression evaluates everything containing nt:
让我们使用FilterType.REGEX,它指示Spring扫描符合regex.*[nt]的类。我们的regex表达式会评估所有包含nt:的内容。
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.REGEX,
pattern = ".*[nt]"))
public class ComponentScanRegexFilterApp { }
This time in our test, we’ll see that Spring scans the Elephant, but not the Lion:
这次在我们的测试中,我们将看到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
6. FilterType.ASPECTJ
When we want to use expressions to pick out a complex subset of classes, we need to use the FilterType ASPECTJ.
当我们想使用表达式来挑选出一个复杂的类子集时,我们需要使用FilterType ASPECTJ。。
For this use case, we can reuse the same three classes as in the previous section.
对于这个用例,我们可以重复使用与上一节相同的三个类。
Let’s use FilterType.ASPECTJ to direct Spring to scan classes that match our AspectJ expression:
让我们使用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 { }
While a bit complex, our logic here wants beans that start with neither “L” nor “C” in their class name, so that leaves us with Elephants again:
虽然有点复杂,但我们的逻辑是希望Bean的类名中既不以 “L “也不以 “C “开头,所以我们又剩下了Elephants。
@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
7.FilterType.CUSTOM
If none of the above filter types meet our requirement then we can also create a custom filter type. For example, let’s say we only want to scan classes whose name is five characters or shorter.
如果上述过滤器类型都不能满足我们的要求,那么 我们也可以创建一个自定义的过滤器类型。例如,假设我们只想扫描名称为五个字符或更短的类。
To create a custom filter, we need to implement the org.springframework.core.type.filter.TypeFilter:
为了创建一个自定义过滤器,我们需要实现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;
}
}
Let’s use FilterType.CUSTOM which conveys Spring to scan classes using our custom filter ComponentScanCustomFilter:
让我们使用FilterType.CUSTOM,它传达了Spring使用我们的自定义过滤器ComponentScanCustomFilter:来扫描类。
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,
classes = ComponentScanCustomFilter.class))
public class ComponentScanCustomFilterApp { }
Now it’s time to see test case of our custom filter ComponentScanCustomFilter:
现在是时候看看我们的自定义过滤器的测试案例了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. Summary
8.摘要
In this tutorial, we introduced filters associated with @ComponentScan.
在本教程中,我们介绍了与@ComponentScan.相关的过滤器。
As usual, the complete code is available over on GitHub.
像往常一样,完整的代码可以在GitHub上找到,。