Classpath Scanning and Managed Components
Most examples in this chapter use XML to specify the configuration metadata that produces
each BeanDefinition
within the Spring container. The previous section
(Annotation-based Container Configuration) demonstrates how to provide a lot of the configuration
metadata through source-level annotations. Even in those examples, however, the "base"
bean definitions are explicitly defined in the XML file, while the annotations drive only
the dependency injection. This section describes an option for implicitly detecting the
candidate components by scanning the classpath. Candidate components are classes that
match against a filter criteria and have a corresponding bean definition registered with
the container. This removes the need to use XML to perform bean registration. Instead, you
can use annotations (for example, @Component
), AspectJ type expressions, or your own
custom filter criteria to select which classes have bean definitions registered with
the container.
You can define beans using Java rather than using XML files. Take a look at the
|
@Component
and Further Stereotype Annotations
The @Repository
annotation is a marker for any class that fulfills the role or
stereotype of a repository (also known as Data Access Object or DAO). Among the uses
of this marker is the automatic translation of exceptions, as described in
Exception Translation.
Spring provides further stereotype annotations: @Component
, @Service
, and
@Controller
. @Component
is a generic stereotype for any Spring-managed component.
@Repository
, @Service
, and @Controller
are specializations of @Component
for
more specific use cases (in the persistence, service, and presentation
layers, respectively). Therefore, you can annotate your component classes with
@Component
, but, by annotating them with @Repository
, @Service
, or @Controller
instead, your classes are more properly suited for processing by tools or associating
with aspects. For example, these stereotype annotations make ideal targets for
pointcuts. @Repository
, @Service
, and @Controller
can also
carry additional semantics in future releases of the Spring Framework. Thus, if you are
choosing between using @Component
or @Service
for your service layer, @Service
is
clearly the better choice. Similarly, as stated earlier, @Repository
is already
supported as a marker for automatic exception translation in your persistence layer.
Using Meta-annotations and Composed Annotations
Many of the annotations provided by Spring can be used as meta-annotations in your
own code. A meta-annotation is an annotation that can be applied to another annotation.
For example, the @Service
annotation mentioned earlier
is meta-annotated with @Component
, as the following example shows:
-
Java
-
Kotlin
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component (1)
public @interface Service {
// ...
}
1 | The @Component causes @Service to be treated in the same way as @Component . |
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {
// ...
}
1 | The @Component causes @Service to be treated in the same way as @Component . |
You can also combine meta-annotations to create “composed annotations”. For example,
the @RestController
annotation from Spring MVC is composed of @Controller
and
@ResponseBody
.
In addition, composed annotations can optionally redeclare attributes from
meta-annotations to allow customization. This can be particularly useful when you
want to only expose a subset of the meta-annotation’s attributes. For example, Spring’s
@SessionScope
annotation hard codes the scope name to session
but still allows
customization of the proxyMode
. The following listing shows the definition of the
SessionScope
annotation:
-
Java
-
Kotlin
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
/**
* Alias for {@link Scope#proxyMode}.
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Scope(WebApplicationContext.SCOPE_SESSION)
annotation class SessionScope(
@get:AliasFor(annotation = Scope::class)
val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)
You can then use @SessionScope
without declaring the proxyMode
as follows:
-
Java
-
Kotlin
@Service
@SessionScope
public class SessionScopedService {
// ...
}
@Service
@SessionScope
class SessionScopedService {
// ...
}
You can also override the value for the proxyMode
, as the following example shows:
-
Java
-
Kotlin
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
// ...
}
For further details, see the Spring Annotation Programming Model wiki page.
Automatically Detecting Classes and Registering Bean Definitions
Spring can automatically detect stereotyped classes and register corresponding
BeanDefinition
instances with the ApplicationContext
. For example, the following two classes
are eligible for such autodetection:
-
Java
-
Kotlin
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
-
Java
-
Kotlin
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
@Repository
class JpaMovieFinder : MovieFinder {
// implementation elided for clarity
}
To autodetect these classes and register the corresponding beans, you need to add
@ComponentScan
to your @Configuration
class, where the basePackages
attribute
is a common parent package for the two classes. (Alternatively, you can specify a
comma- or semicolon- or space-separated list that includes the parent package of each class.)
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig {
// ...
}
For brevity, the preceding example could have used the value attribute of the
annotation (that is, @ComponentScan("org.example") ).
|
The following alternative uses XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
The use of <context:component-scan> implicitly enables the functionality of
<context:annotation-config> . There is usually no need to include the
<context:annotation-config> element when using <context:component-scan> .
|
The scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When you build JARs with Ant, make sure that you do not activate the files-only switch of the JAR task. Also, classpath directories may not be exposed based on security policies in some environments — for example, standalone apps on JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests — see stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources). On JDK 9’s module path (Jigsaw), Spring’s classpath scanning generally works as expected.
However, make sure that your component classes are exported in your |
Furthermore, the AutowiredAnnotationBeanPostProcessor
and
CommonAnnotationBeanPostProcessor
are both implicitly included when you use the
component-scan element. That means that the two components are autodetected and
wired together — all without any bean configuration metadata provided in XML.
You can disable the registration of AutowiredAnnotationBeanPostProcessor and
CommonAnnotationBeanPostProcessor by including the annotation-config attribute
with a value of false .
|
Using Filters to Customize Scanning
By default, classes annotated with @Component
, @Repository
, @Service
, @Controller
,
@Configuration
, or a custom annotation that itself is annotated with @Component
are
the only detected candidate components. However, you can modify and extend this behavior
by applying custom filters. Add them as includeFilters
or excludeFilters
attributes of
the @ComponentScan
annotation (or as <context:include-filter />
or
<context:exclude-filter />
child elements of the <context:component-scan>
element in
XML configuration). Each filter element requires the type
and expression
attributes.
The following table describes the filtering options:
Filter Type | Example Expression | Description |
---|---|---|
annotation (default) |
|
An annotation to be present or meta-present at the type level in target components. |
assignable |
|
A class (or interface) that the target components are assignable to (extend or implement). |
aspectj |
|
An AspectJ type expression to be matched by the target components. |
regex |
|
A regex expression to be matched by the target components' class names. |
custom |
|
A custom implementation of the |
The following example shows the configuration ignoring all @Repository
annotations
and using “stub” repositories instead:
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"],
includeFilters = [Filter(type = FilterType.REGEX, pattern = [".*Stub.*Repository"])],
excludeFilters = [Filter(Repository::class)])
class AppConfig {
// ...
}
The following listing shows the equivalent XML:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
You can also disable the default filters by setting useDefaultFilters=false on the
annotation or by providing use-default-filters="false" as an attribute of the
<component-scan/> element. This effectively disables automatic detection of classes
annotated or meta-annotated with @Component , @Repository , @Service , @Controller ,
@RestController , or @Configuration .
|
Defining Bean Metadata within Components
Spring components can also contribute bean definition metadata to the container. You can do
this with the same @Bean
annotation used to define bean metadata within @Configuration
annotated classes. The following example shows how to do so:
-
Java
-
Kotlin
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
@Component
class FactoryMethodComponent {
@Bean
@Qualifier("public")
fun publicInstance() = TestBean("publicInstance")
fun doWork() {
// Component method implementation omitted
}
}
The preceding class is a Spring component that has application-specific code in its
doWork()
method. However, it also contributes a bean definition that has a factory
method referring to the method publicInstance()
. The @Bean
annotation identifies the
factory method and other bean definition properties, such as a qualifier value through
the @Qualifier
annotation. Other method-level annotations that can be specified are
@Scope
, @Lazy
, and custom qualifier annotations.
In addition to its role for component initialization, you can also place the @Lazy
annotation on injection points marked with @Autowired or @Inject . In this context,
it leads to the injection of a lazy-resolution proxy. However, such a proxy approach
is rather limited. For sophisticated lazy interactions, in particular in combination
with optional dependencies, we recommend ObjectProvider<MyTargetBean> instead.
|
Autowired fields and methods are supported, as previously discussed, with additional
support for autowiring of @Bean
methods. The following example shows how to do so:
-
Java
-
Kotlin
@Component
public class FactoryMethodComponent {
private static int i;
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
// use of a custom qualifier and autowiring of method parameters
@Bean
protected TestBean protectedInstance(
@Qualifier("public") TestBean spouse,
@Value("#{privateInstance.age}") String country) {
TestBean tb = new TestBean("protectedInstance", 1);
tb.setSpouse(spouse);
tb.setCountry(country);
return tb;
}
@Bean
private TestBean privateInstance() {
return new TestBean("privateInstance", i++);
}
@Bean
@RequestScope
public TestBean requestScopedInstance() {
return new TestBean("requestScopedInstance", 3);
}
}
@Component
class FactoryMethodComponent {
companion object {
private var i: Int = 0
}
@Bean
@Qualifier("public")
fun publicInstance() = TestBean("publicInstance")
// use of a custom qualifier and autowiring of method parameters
@Bean
protected fun protectedInstance(
@Qualifier("public") spouse: TestBean,
@Value("#{privateInstance.age}") country: String) = TestBean("protectedInstance", 1).apply {
this.spouse = spouse
this.country = country
}
@Bean
private fun privateInstance() = TestBean("privateInstance", i++)
@Bean
@RequestScope
fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}
The example autowires the String
method parameter country
to the value of the age
property on another bean named privateInstance
. A Spring Expression Language element
defines the value of the property through the notation #{ <expression> }
. For @Value
annotations, an expression resolver is preconfigured to look for bean names when
resolving expression text.
As of Spring Framework 4.3, you may also declare a factory method parameter of type
InjectionPoint
(or its more specific subclass: DependencyDescriptor
) to
access the requesting injection point that triggers the creation of the current bean.
Note that this applies only to the actual creation of bean instances, not to the
injection of existing instances. As a consequence, this feature makes most sense for
beans of prototype scope. For other scopes, the factory method only ever sees the
injection point that triggered the creation of a new bean instance in the given scope
(for example, the dependency that triggered the creation of a lazy singleton bean).
You can use the provided injection point metadata with semantic care in such scenarios.
The following example shows how to use InjectionPoint
:
-
Java
-
Kotlin
@Component
public class FactoryMethodComponent {
@Bean @Scope("prototype")
public TestBean prototypeInstance(InjectionPoint injectionPoint) {
return new TestBean("prototypeInstance for " + injectionPoint.getMember());
}
}
@Component
class FactoryMethodComponent {
@Bean
@Scope("prototype")
fun prototypeInstance(injectionPoint: InjectionPoint) =
TestBean("prototypeInstance for ${injectionPoint.member}")
}
The @Bean
methods in a regular Spring component are processed differently than their
counterparts inside a Spring @Configuration
class. The difference is that @Component
classes are not enhanced with CGLIB to intercept the invocation of methods and fields.
CGLIB proxying is the means by which invoking methods or fields within @Bean
methods
in @Configuration
classes creates bean metadata references to collaborating objects.
Such methods are not invoked with normal Java semantics but rather go through the
container in order to provide the usual lifecycle management and proxying of Spring
beans, even when referring to other beans through programmatic calls to @Bean
methods.
In contrast, invoking a method or field in a @Bean
method within a plain @Component
class has standard Java semantics, with no special CGLIB processing or other
constraints applying.
You may declare Calls to static The Java language visibility of
Finally, a single class may hold multiple |
Naming Autodetected Components
When a component is autodetected as part of the scanning process, its bean name is
generated by the BeanNameGenerator
strategy known to that scanner. By default, any
Spring stereotype annotation (@Component
, @Repository
, @Service
, and
@Controller
) that contains a name value
thereby provides that name to the
corresponding bean definition.
If such an annotation contains no name value
or for any other detected component
(such as those discovered by custom filters), the default bean name generator returns
the uncapitalized non-qualified class name. For example, if the following component
classes were detected, the names would be myMovieLister
and movieFinderImpl
:
-
Java
-
Kotlin
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
// ...
}
-
Java
-
Kotlin
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
If you do not want to rely on the default bean-naming strategy, you can provide a custom
bean-naming strategy. First, implement the
BeanNameGenerator
interface, and be sure to include a default no-arg constructor. Then, provide the fully
qualified class name when configuring the scanner, as the following example annotation
and bean definition show.
If you run into naming conflicts due to multiple autodetected components having the
same non-qualified class name (i.e., classes with identical names but residing in
different packages), you may need to configure a BeanNameGenerator that defaults to the
fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, the
FullyQualifiedAnnotationBeanNameGenerator located in package
org.springframework.context.annotation can be used for such purposes.
|
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
As a general rule, consider specifying the name with the annotation whenever other components may be making explicit references to it. On the other hand, the auto-generated names are adequate whenever the container is responsible for wiring.
Providing a Scope for Autodetected Components
As with Spring-managed components in general, the default and most common scope for
autodetected components is singleton
. However, sometimes you need a different scope
that can be specified by the @Scope
annotation. You can provide the name of the
scope within the annotation, as the following example shows:
-
Java
-
Kotlin
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
@Scope annotations are only introspected on the concrete bean class (for annotated
components) or the factory method (for @Bean methods). In contrast to XML bean
definitions, there is no notion of bean definition inheritance, and inheritance
hierarchies at the class level are irrelevant for metadata purposes.
|
For details on web-specific scopes such as “request” or “session” in a Spring context,
see Request, Session, Application, and WebSocket Scopes. As with the pre-built annotations for those scopes,
you may also compose your own scoping annotations by using Spring’s meta-annotation
approach: for example, a custom annotation meta-annotated with @Scope("prototype")
,
possibly also declaring a custom scoped-proxy mode.
To provide a custom strategy for scope resolution rather than relying on the
annotation-based approach, you can implement the
ScopeMetadataResolver
interface. Be sure to include a default no-arg constructor. Then you can provide the
fully qualified class name when configuring the scanner, as the following example of both
an annotation and a bean definition shows:
|
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
When using certain non-singleton scopes, it may be necessary to generate proxies for the
scoped objects. The reasoning is described in Scoped Beans as Dependencies.
For this purpose, a scoped-proxy attribute is available on the component-scan
element. The three possible values are: no
, interfaces
, and targetClass
. For example,
the following configuration results in standard JDK dynamic proxies:
-
Java
-
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
Providing Qualifier Metadata with Annotations
The @Qualifier
annotation is discussed in Fine-tuning Annotation-based Autowiring with Qualifiers.
The examples in that section demonstrate the use of the @Qualifier
annotation and
custom qualifier annotations to provide fine-grained control when you resolve autowire
candidates. Because those examples were based on XML bean definitions, the qualifier
metadata was provided on the candidate bean definitions by using the qualifier
or meta
child elements of the bean
element in the XML. When relying upon classpath scanning for
auto-detection of components, you can provide the qualifier metadata with type-level
annotations on the candidate class. The following three examples demonstrate this
technique:
-
Java
-
Kotlin
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
-
Java
-
Kotlin
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
// ...
}
-
Java
-
Kotlin
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
// ...
}
As with most annotation-based alternatives, keep in mind that the annotation metadata is bound to the class definition itself, while the use of XML allows for multiple beans of the same type to provide variations in their qualifier metadata, because that metadata is provided per-instance rather than per-class. |
Generating an Index of Candidate Components
While classpath scanning is very fast, it is possible to improve the startup performance of large applications by creating a static list of candidates at compilation time. In this mode, all modules that are targets of component scanning must use this mechanism.
Your existing @ComponentScan or <context:component-scan/> directives must remain
unchanged to request the context to scan candidates in certain packages. When the
ApplicationContext detects such an index, it automatically uses it rather than scanning
the classpath.
|
To generate the index, add an additional dependency to each module that contains components that are targets for component scan directives. The following example shows how to do so with Maven:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>6.0.26</version>
<optional>true</optional>
</dependency>
</dependencies>
With Gradle 4.5 and earlier, the dependency should be declared in the compileOnly
configuration, as shown in the following example:
dependencies {
compileOnly "org.springframework:spring-context-indexer:6.0.26"
}
With Gradle 4.6 and later, the dependency should be declared in the annotationProcessor
configuration, as shown in the following example:
dependencies {
annotationProcessor "org.springframework:spring-context-indexer:6.0.26"
}
The spring-context-indexer
artifact generates a META-INF/spring.components
file that
is included in the jar file.
When working with this mode in your IDE, the spring-context-indexer must be
registered as an annotation processor to make sure the index is up-to-date when
candidate components are updated.
|
The index is enabled automatically when a META-INF/spring.components file is found
on the classpath. If an index is partially available for some libraries (or use cases)
but could not be built for the whole application, you can fall back to a regular classpath
arrangement (as though no index were present at all) by setting spring.index.ignore to
true , either as a JVM system property or via the
SpringProperties mechanism.
|