Перейти к основному содержимому

Spring @ComponentScan — типы фильтров

· 5 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

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 .