Restructure and polish the classpath scanning chapter
This commit is contained in:
parent
dbb9bf939c
commit
abdc3200b2
|
@ -9,7 +9,7 @@ annotations. Even in those examples, however, the "base" bean definitions are ex
|
||||||
defined in the XML file, while the annotations drive only the dependency injection.
|
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
|
This section describes an option for implicitly detecting the candidate components by
|
||||||
scanning the classpath. Candidate components are classes that match against a filter
|
scanning the classpath. Candidate components are classes that match against filter
|
||||||
criteria and have a corresponding bean definition registered with the container.
|
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
|
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
|
annotations (for example, `@Component`), AspectJ type expressions, or your own
|
||||||
|
@ -70,7 +70,7 @@ Java::
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
|
<1> The `@Component` meta-annotation causes `@Service` to be treated in the same way as `@Component`.
|
||||||
|
|
||||||
Kotlin::
|
Kotlin::
|
||||||
+
|
+
|
||||||
|
@ -85,7 +85,7 @@ Kotlin::
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
|
<1> The `@Component` meta-annotation causes `@Service` to be treated in the same way as `@Component`.
|
||||||
======
|
======
|
||||||
|
|
||||||
You can also combine meta-annotations to create "`composed annotations`". For example,
|
You can also combine meta-annotations to create "`composed annotations`". For example,
|
||||||
|
@ -97,7 +97,7 @@ meta-annotations to allow customization. This can be particularly useful when yo
|
||||||
want to only expose a subset of the meta-annotation's attributes. For example, Spring's
|
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
|
`@SessionScope` annotation hard codes the scope name to `session` but still allows
|
||||||
customization of the `proxyMode`. The following listing shows the definition of the
|
customization of the `proxyMode`. The following listing shows the definition of the
|
||||||
`SessionScope` annotation:
|
`@SessionScope` annotation:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -211,7 +211,7 @@ Java::
|
||||||
@Service
|
@Service
|
||||||
public class SimpleMovieLister {
|
public class SimpleMovieLister {
|
||||||
|
|
||||||
private MovieFinder movieFinder;
|
private final MovieFinder movieFinder;
|
||||||
|
|
||||||
public SimpleMovieLister(MovieFinder movieFinder) {
|
public SimpleMovieLister(MovieFinder movieFinder) {
|
||||||
this.movieFinder = movieFinder;
|
this.movieFinder = movieFinder;
|
||||||
|
@ -251,11 +251,11 @@ Kotlin::
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|
||||||
To autodetect these classes and register the corresponding beans, you need to add
|
To autodetect these classes and register the corresponding beans, you need to add
|
||||||
`@ComponentScan` to your `@Configuration` class, where the `basePackages` attribute
|
`@ComponentScan` to your `@Configuration` class, where the `basePackages` attribute is
|
||||||
is a common parent package for the two classes. (Alternatively, you can specify a
|
configured with a common parent package for the two classes. Alternatively, you can
|
||||||
comma- or semicolon- or space-separated list that includes the parent package of each class.)
|
specify a comma-, semicolon-, or space-separated list that includes the parent package
|
||||||
|
of each class.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -282,10 +282,10 @@ Kotlin::
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
NOTE: For brevity, the preceding example could have used the `value` attribute of the
|
TIP: For brevity, the preceding example could have used the implicit `value` attribute of
|
||||||
annotation (that is, `@ComponentScan("org.example")`).
|
the annotation instead: `@ComponentScan("org.example")`
|
||||||
|
|
||||||
The following alternative uses XML:
|
The following example uses XML configuration:
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -386,7 +386,7 @@ app.scan.packages=org.example.config, org.example.service.**
|
||||||
|
|
||||||
|
|
||||||
[[beans-scanning-filters]]
|
[[beans-scanning-filters]]
|
||||||
== Using Filters to Customize Scanning
|
=== Using Filters to Customize Scanning
|
||||||
|
|
||||||
By default, classes annotated with `@Component`, `@Repository`, `@Service`, `@Controller`,
|
By default, classes annotated with `@Component`, `@Repository`, `@Service`, `@Controller`,
|
||||||
`@Configuration`, or a custom annotation that itself is annotated with `@Component` are
|
`@Configuration`, or a custom annotation that itself is annotated with `@Component` are
|
||||||
|
@ -423,8 +423,8 @@ The following table describes the filtering options:
|
||||||
| A custom implementation of the `org.springframework.core.type.TypeFilter` interface.
|
| A custom implementation of the `org.springframework.core.type.TypeFilter` interface.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
The following example shows the configuration ignoring all `@Repository` annotations
|
The following example shows `@ComponentScan` configuration that excludes all
|
||||||
and using "`stub`" repositories instead:
|
`@Repository` annotations and includes "`Stub`" repositories instead:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -476,6 +476,353 @@ annotated or meta-annotated with `@Component`, `@Repository`, `@Service`, `@Cont
|
||||||
`@RestController`, or `@Configuration`.
|
`@RestController`, or `@Configuration`.
|
||||||
|
|
||||||
|
|
||||||
|
[[beans-scanning-name-generator]]
|
||||||
|
=== 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, the `AnnotationBeanNameGenerator` is used. For Spring
|
||||||
|
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
|
||||||
|
if you supply a name via the annotation's `value` attribute that name will be used as
|
||||||
|
the name in the corresponding bean definition. This convention also applies when the
|
||||||
|
`@jakarta.inject.Named` annotation is used instead of Spring stereotype annotations.
|
||||||
|
|
||||||
|
As of Spring Framework 6.1, the name of the annotation attribute that is used to specify
|
||||||
|
the bean name is no longer required to be `value`. Custom stereotype annotations can
|
||||||
|
declare an attribute with a different name (such as `name`) and annotate that attribute
|
||||||
|
with `@AliasFor(annotation = Component.class, attribute = "value")`. See the source code
|
||||||
|
declaration of `ControllerAdvice#name()` for a concrete example.
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
====
|
||||||
|
As of Spring Framework 6.1, support for convention-based stereotype names is deprecated
|
||||||
|
and will be removed in a future version of the framework. Consequently, custom stereotype
|
||||||
|
annotations must use `@AliasFor` to declare an explicit alias for the `value` attribute
|
||||||
|
in `@Component`. See the source code declaration of `Repository#value()` and
|
||||||
|
`ControllerAdvice#name()` for concrete examples.
|
||||||
|
====
|
||||||
|
|
||||||
|
If an explicit bean name cannot be derived from such an annotation 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`.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Service("myMovieLister")
|
||||||
|
public class SimpleMovieLister {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Service("myMovieLister")
|
||||||
|
class SimpleMovieLister {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Repository
|
||||||
|
public class MovieFinderImpl implements MovieFinder {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@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
|
||||||
|
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`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.
|
||||||
|
|
||||||
|
TIP: 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. The
|
||||||
|
`FullyQualifiedAnnotationBeanNameGenerator` located in package
|
||||||
|
`org.springframework.context.annotation` can be used for such purposes.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
|
||||||
|
public class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
|
||||||
|
class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
<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.
|
||||||
|
|
||||||
|
|
||||||
|
[[beans-scanning-scope-resolver]]
|
||||||
|
=== 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:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Scope("prototype")
|
||||||
|
@Repository
|
||||||
|
public class MovieFinderImpl implements MovieFinder {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Scope("prototype")
|
||||||
|
@Repository
|
||||||
|
class MovieFinderImpl : MovieFinder {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
NOTE: `@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 xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other[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.
|
||||||
|
|
||||||
|
NOTE: To provide a custom strategy for scope resolution rather than relying on the
|
||||||
|
annotation-based approach, you can implement the
|
||||||
|
{spring-framework-api}/context/annotation/ScopeMetadataResolver.html[`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:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
|
||||||
|
public class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
|
||||||
|
class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
<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
|
||||||
|
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[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:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
|
||||||
|
public class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
|
||||||
|
class AppConfig {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
<beans>
|
||||||
|
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
|
||||||
|
</beans>
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
[[beans-scanning-qualifiers]]
|
||||||
|
=== Providing Qualifier Metadata with Annotations
|
||||||
|
|
||||||
|
The `@Qualifier` annotation is discussed in
|
||||||
|
xref:core/beans/annotation-config/autowired-qualifiers.adoc[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:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Qualifier("Action")
|
||||||
|
public class ActionMovieCatalog implements MovieCatalog {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Qualifier("Action")
|
||||||
|
class ActionMovieCatalog : MovieCatalog
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Genre("Action")
|
||||||
|
public class ActionMovieCatalog implements MovieCatalog {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Genre("Action")
|
||||||
|
class ActionMovieCatalog : MovieCatalog {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Offline
|
||||||
|
public class CachingMovieCatalog implements MovieCatalog {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@Component
|
||||||
|
@Offline
|
||||||
|
class CachingMovieCatalog : MovieCatalog {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
NOTE: 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.
|
||||||
|
|
||||||
|
|
||||||
[[beans-factorybeans-annotations]]
|
[[beans-factorybeans-annotations]]
|
||||||
== Defining Bean Metadata within Components
|
== Defining Bean Metadata within Components
|
||||||
|
|
||||||
|
@ -710,350 +1057,3 @@ constructor or factory method in other configuration scenarios: The variant with
|
||||||
the largest number of satisfiable dependencies is picked at construction time,
|
the largest number of satisfiable dependencies is picked at construction time,
|
||||||
analogous to how the container selects between multiple `@Autowired` constructors.
|
analogous to how the container selects between multiple `@Autowired` constructors.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
||||||
[[beans-scanning-name-generator]]
|
|
||||||
== 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, the `AnnotationBeanNameGenerator` is used. For Spring
|
|
||||||
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
|
|
||||||
if you supply a name via the annotation's `value` attribute that name will be used as
|
|
||||||
the name in the corresponding bean definition. This convention also applies when the
|
|
||||||
`@jakarta.inject.Named` annotation is used instead of Spring stereotype annotations.
|
|
||||||
|
|
||||||
As of Spring Framework 6.1, the name of the annotation attribute that is used to specify
|
|
||||||
the bean name is no longer required to be `value`. Custom stereotype annotations can
|
|
||||||
declare an attribute with a different name (such as `name`) and annotate that attribute
|
|
||||||
with `@AliasFor(annotation = Component.class, attribute = "value")`. See the source code
|
|
||||||
declaration of `ControllerAdvice#name()` for a concrete example.
|
|
||||||
|
|
||||||
[WARNING]
|
|
||||||
====
|
|
||||||
As of Spring Framework 6.1, support for convention-based stereotype names is deprecated
|
|
||||||
and will be removed in a future version of the framework. Consequently, custom stereotype
|
|
||||||
annotations must use `@AliasFor` to declare an explicit alias for the `value` attribute
|
|
||||||
in `@Component`. See the source code declaration of `Repository#value()` and
|
|
||||||
`ControllerAdvice#name()` for concrete examples.
|
|
||||||
====
|
|
||||||
|
|
||||||
If an explicit bean name cannot be derived from such an annotation 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`.
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Service("myMovieLister")
|
|
||||||
public class SimpleMovieLister {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Service("myMovieLister")
|
|
||||||
class SimpleMovieLister {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Repository
|
|
||||||
public class MovieFinderImpl implements MovieFinder {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@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
|
|
||||||
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`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.
|
|
||||||
|
|
||||||
TIP: 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. The
|
|
||||||
`FullyQualifiedAnnotationBeanNameGenerator` located in package
|
|
||||||
`org.springframework.context.annotation` can be used for such purposes.
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
|
|
||||||
public class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
|
|
||||||
class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
<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.
|
|
||||||
|
|
||||||
|
|
||||||
[[beans-scanning-scope-resolver]]
|
|
||||||
== 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:
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Scope("prototype")
|
|
||||||
@Repository
|
|
||||||
public class MovieFinderImpl implements MovieFinder {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Scope("prototype")
|
|
||||||
@Repository
|
|
||||||
class MovieFinderImpl : MovieFinder {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
NOTE: `@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 xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other[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.
|
|
||||||
|
|
||||||
NOTE: To provide a custom strategy for scope resolution rather than relying on the
|
|
||||||
annotation-based approach, you can implement the
|
|
||||||
{spring-framework-api}/context/annotation/ScopeMetadataResolver.html[`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:
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
|
|
||||||
public class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
|
|
||||||
class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
<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
|
|
||||||
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[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:
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
|
|
||||||
public class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
|
|
||||||
class AppConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
<beans>
|
|
||||||
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
|
|
||||||
</beans>
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
[[beans-scanning-qualifiers]]
|
|
||||||
== Providing Qualifier Metadata with Annotations
|
|
||||||
|
|
||||||
The `@Qualifier` annotation is discussed in
|
|
||||||
xref:core/beans/annotation-config/autowired-qualifiers.adoc[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:
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Qualifier("Action")
|
|
||||||
public class ActionMovieCatalog implements MovieCatalog {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Qualifier("Action")
|
|
||||||
class ActionMovieCatalog : MovieCatalog
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Genre("Action")
|
|
||||||
public class ActionMovieCatalog implements MovieCatalog {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Genre("Action")
|
|
||||||
class ActionMovieCatalog : MovieCatalog {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Offline
|
|
||||||
public class CachingMovieCatalog implements MovieCatalog {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@Component
|
|
||||||
@Offline
|
|
||||||
class CachingMovieCatalog : MovieCatalog {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
NOTE: 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.
|
|
||||||
|
|
Loading…
Reference in New Issue