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);
}
}