1. Обзор
В предыдущем уроке мы узнали об основах сканирования компонентов Spring .
В этой статье мы увидим различные типы параметров фильтрации, доступные с аннотацией @ComponentScan
.
2. @ Фильтр сканирования компонентов
По умолчанию классы, аннотированные @Component, @Repository, @Service, @Controller
, регистрируются как Spring bean -компоненты . То же самое касается классов, аннотированных пользовательской аннотацией, которая аннотирована с помощью @Component
. Мы можем расширить это поведение, используя параметры includeFilters и
excludeFilters
аннотации @ComponentScan .
Для ComponentScan.Filter
доступно пять типов фильтров :
АННОТАЦИЯ
ASSIGNABLE_TYPE
АСПЕКТJ
РЕГЭКС
ОБЫЧАЙ
Мы подробно рассмотрим их в следующих разделах.
Стоит отметить, что все эти фильтры могут включать или исключать классы из сканирования. Для простоты в наших примерах мы будем включать только классы.
3. Тип фильтра.АННОТАЦИЯ
Тип фильтра АННОТАЦИЯ
включает или исключает классы в сканах компонентов, помеченных заданными аннотациями.
Допустим, например, что у нас есть аннотация @Animal
:
@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 { }
Как мы видим, сканер отлично распознает нашего Слона
:
@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. Тип фильтра.REGEX
Фильтр REGEX
проверяет, соответствует ли имя класса заданному шаблону регулярного выражения. FilterType.REGEX
проверяет как простые, так и полные имена классов.
Еще раз давайте объявим наш класс Elephant .
На этот раз не реализуя какой-либо интерфейс или аннотируя какую-либо аннотацию :
public class Elephant { }
Объявим еще один класс Cat
:
public class Cat { }
Теперь давайте объявим класс Loin :
public class Loin { }
Давайте использовать FilterType
. REGEX
, который указывает 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. Тип фильтра.ASPECTJ
Когда мы хотим использовать выражения для выбора сложного подмножества классов, нам нужно использовать ASPECTJ
FilterType
.
Для этого варианта использования мы можем повторно использовать те же три класса, что и в предыдущем разделе.
Давайте используем FilterType.ASPECTJ
, чтобы указать Spring для сканирования классов, которые соответствуют нашему выражению AspectJ :
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASPECTJ,
pattern = "com.foreach.componentscan.filter.aspectj.* "
+ "&& !(com.foreach.componentscan.filter.aspectj.L* "
+ "|| com.foreach.componentscan.filter.aspectj.C*)"))
public class ComponentScanAspectJFilterApp { }
Хотя это немного сложно, наша логика здесь требует, чтобы bean-компоненты не начинались ни с «L», ни с «C» в своем имени класса, так что это снова оставляет нас с Elephant s:
@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. Тип фильтра.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.
Как обычно, полный код доступен на GitHub .