Selectively create Beans with @ComponentScan

How to create Beans without using default annotations

The usual way

In Spring applications we usually create Beans using the annotations @Component, @Service, @Repository or @Controller. Classes having one of these annotations are detected by Spring and instantiated as Spring Beans with all dependency injection taking place.

@Service
class MySuperService {
...
}

The same is true for custom annotations which are themselves annotated with @Component.

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

Using @ComponentScan

When defining a configuration class, we can configure in which packages (and sub-packages) Spring should search for Beans. By default all the classes with the above mentioned annotations are found.

@Configuration
@ComponentScan(basePackages = {"de.mindstack.mypackage.beans"})
public class MyConfiguration {}

If we want to exclude the standard beans we have to set the parameter useDefaultFilters to false.

@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false)
public class MyConfiguration {}

IncludeFilters

With the default filters deactivated, we can now define our own filters to find classes we want to instantiate as Beans. There a five types of filters available:

  • ANNOTATION - classes having a specific annotation
  • ASSIGNABLE_TYPE - classes which derive from a specific class or implement a specific interface
  • REGEX - classes which match a given regular expression
  • ASPECTJ - classes which match complex AspectJ patterns
  • CUSTOM - custom filter implementation

FilterType.ANNOTATION

When we created a custom annotation and want all classes with this annotation to be instantiated as beans, we can use this filter.

// Declaring an annotation to be used with class that should be instantiated as beans
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyBeanAnnotation {}

// Class annotated with custom annotation will be instantiated as bean
@MyBeanAnnotation
public class MyClass {}

@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false,
  includeFilters = @ComponentScan.Filter(
    type = FilterType.ANNOTATION,
    classes = MyBeanAnnotation.class)
public class MyConfiguration {}

FilterType.ASSIGNABLE_TYPE

When we base class and want all subclasses or implementation of an interface to be instantiated as beans, we can use this filter.

interface MyBaseClass {}

// This class implements MyBaseClass and will be instantiated as bean
public class SubClassA implements MyBaseClass {}

// This class implements MyBaseClass and will be instantiated as bean
public class SubClassB implements MyBaseClass {}


@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false,
  includeFilters = @ComponentScan.Filter(
    type = FilterType.ASSIGNABLE_TYPE,
    classes = MyBaseClass.class)
public class MyConfiguration {}

FilterType.REGEX

We can select class names by matching regular expressions agains simple and fully-qualified class names.

// This class name matches the regex and will be instantiated as bean
public class MyMatchedClass {}

// This class name does not match the regex and will not be instantiated as bean
public class IgnoredClass {}


@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false,
  includeFilters = @ComponentScan.Filter(
    type = FilterType.REGEX,
    pattern = "Match")
public class MyConfiguration {}

FilterType.ASPECTJ

We can select classes by matching aspectj expressions.

// This class name matches the expression and will be instantiated as bean
public class MyMatchedClass {}

// This class name does not match the expression and will not be instantiated as bean
public class IgnoredClass {}


@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false,
  includeFilters = @ComponentScan.Filter(
    type = FilterType.ASPECTJ,
    pattern = "de.mindstack.mypackage.beans.My*")
public class MyConfiguration {}

FilterType.CUSTOM

We can implement our own filters by implementing TypeFilter inrerface. This way, we can create combined filters.

// This class name matches the expression and will be instantiated as bean
@MyBeanAnnotation
public class MyMatchedClass implements MyBaseClass {}

// This class name does not match the expression and will not be instantiated as bean
@MyBeanAnnotation
public class IgnoredClass {}


@Configuration
@ComponentScan(
  basePackages = {"de.mindstack.mypackage.beans"},
  useDefaultFilters = false,
  includeFilters = @ComponentScan.Filter(
    type = FilterType.CUSTOM,
    classes = CombinedFilter.class)
public class MyConfiguration {}

public class CombinedFilter implements TypeFilter {
  private final AssignableTypeFilter assignableTypeFilter;
  private final AnnotationTypeFilter annotationTypeFilter;

  public CombinedFilter() {
    assignableTypeFilter = new AssignableTypeFilter(MyBaseClass.class);
    annotationTypeFilter = new AnnotationTypeFilter(MyBeanAnnotation.class);
  }

  @Override
  public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    return annotationTypeFilter.match(metadataReader, metadataReaderFactory)
        && assignableTypeFilter.match(metadataReader, metadataReaderFactory);
  }
}
Built with Hugo
Theme Stack designed by Jimmy