Classpath Scanning and Managed Components
1.10. 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.
Starting with Spring 3.0, many features provided by the Spring JavaConfig project are part of the core Spring Framework. This allows you to define beans using Java rather than using the traditional XML files. Take a look at the
@Configuration,@Bean,@Import, and@DependsOnannotations for examples of how to use these new features.
1.10.1. `@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.
1.10.2. 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:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
// ...
}
- The
@Componentcauses@Serviceto 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 hardcodes the scope name to session but still allows customization of the proxyMode. The following listing shows the definition of the SessionScope annotation:
@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;
}
You can then use @SessionScope without declaring the proxyMode as follows:
@Service
@SessionScope
public class SessionScopedService {
// ...
}
You can also override the value for the proxyMode, as the following example shows:
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
For further details, see the Spring Annotation Programming Model wiki page.
1.10.3. 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:
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Repository
public class JpaMovieFinder implements 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.)
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
For brevity, the preceding example could have used the
valueattribute 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>.
<context:component-scan>的使用隐式启用了<context:annotation-config>的功能。当使用<context:component-scan>时,通常不需要包含<context:annotation-config>元素。
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 https://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
module-infodescriptors. If you expect Spring to invoke non-public members of your classes, make sure that they are 'opened' (that is, that they use anopensdeclaration instead of anexportsdeclaration in yourmodule-infodescriptor).
在JDK 9的模块路径(Jigsaw)上,Spring的类路径扫描通常按预期工作。但是,要确保组件类是在模块信息描述符中导出的。如果您希望Spring调用类的非公有成员,请确保它们是“打开的”(也就是说,它们在模块信息描述符中使用打开声明而不是导出声明)。
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
AutowiredAnnotationBeanPostProcessorandCommonAnnotationBeanPostProcessorby including theannotation-configattribute with a value offalse.
1.10.4. 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:
<context:include-filter />或<context:exclude-filter />子元素)<context: component-scan>元素在XML配置中)。每个筛选器元素都需要类型和表达式属性。过滤选项说明如下表所示:
| Filter Type | Example Expression | Description | 说明 |
|---|---|---|---|
| annotation (default) | org.example.SomeAnnotation |
An annotation to be present or meta-present at the type level in target components. | 在目标组件的类型级别上是present或meta-present的注解。 |
| assignable | org.example.SomeClass |
A class (or interface) that the target components are assignable to (extend or implement). | 目标组件可分配给(扩展或实现)的类(或接口)。 |
| aspectj | org.example..*Service+ |
An AspectJ type expression to be matched by the target components. | 目标组件要匹配的AspectJ类型表达式。 |
| regex | org\.example\.Default.* |
A regex expression to be matched by the target components' class names. | 由目标组件的类名匹配的regex表达式。 |
| custom | org.example.MyTypeFilter |
A custom implementation of the org.springframework.core.type.TypeFilter interface. |
接口org.springframework.core.type.TypeFilter的自定义实现。 |
The following example shows the configuration ignoring all @Repository annotations and using “stub” repositories instead:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public 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=falseon the annotation or by providinguse-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.
1.10.5. 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:
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void 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
@Lazyannotation on injection points marked with@Autowiredor@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 recommendObjectProvider<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:
@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);
}
}
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:
@Component
public class FactoryMethodComponent {
@Bean @Scope("prototype")
public TestBean prototypeInstance(InjectionPoint injectionPoint) {
return new TestBean("prototypeInstance for " + injectionPoint.getMember());
}
}
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
@Beanmethods asstatic, allowing for them to be called without creating their containing configuration class as an instance. This makes particular sense when defining post-processor beans (for example, of typeBeanFactoryPostProcessororBeanPostProcessor), since such beans get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.Calls to static
@Beanmethods never get intercepted by the container, not even within@Configurationclasses (as described earlier in this section), due to technical limitations: CGLIB subclassing can override only non-static methods. As a consequence, a direct call to another@Beanmethod has standard Java semantics, resulting in an independent instance being returned straight from the factory method itself.The Java language visibility of
@Beanmethods does not have an immediate impact on the resulting bean definition in Spring’s container. You can freely declare your factory methods as you see fit in non-@Configurationclasses and also for static methods anywhere. However, regular@Beanmethods in@Configurationclasses need to be overridable — that is, they must not be declared asprivateorfinal.
@Beanmethods are also discovered on base classes of a given component or configuration class, as well as on Java 8 default methods declared in interfaces implemented by the component or configuration class. This allows for a lot of flexibility in composing complex configuration arrangements, with even multiple inheritance being possible through Java 8 default methods as of Spring 4.2.Finally, a single class may hold multiple
@Beanmethods for the same bean, as an arrangement of multiple factory methods to use depending on available dependencies at runtime. This is the same algorithm as for choosing the “greediest” constructor or factory method in other configuration scenarios: The variant with the largest number of satisfiable dependencies is picked at construction time, analogous to how the container selects between multiple@Autowiredconstructors.
由于技术限制:CGLIB子类只能重写非静态方法,对静态@Bean方法的调用永远不会被容器截获,甚至在@Configuration类中也不会(如本节前面所述)。因此,直接调用另一个@Bean方法具有标准的Java语义,从而直接从工厂方法本身返回一个独立的实例。
@Bean方法的Java语言可见性不会对Spring容器中的结果bean定义产生直接影响。您可以在non-@Configuration类中自由声明您的工厂方法,也可以在任何地方声明静态方法。然而,@Configuration类中的常规@Bean方法需要是可重写的—也就是说,它们不能被声明为private或final。
@Bean方法还可以在给定组件或配置类的基类中发现,以及在组件或配置类实现的接口中声明的Java 8默认方法中发现。这为组合复杂的配置安排提供了很大的灵活性,甚至可以通过Spring 4.2的Java 8默认方法实现多重继承。
最后,一个类可以为同一个bean保存多个@Bean方法,这是在运行时根据可用依赖项使用多个工厂方法的安排。这与在其他配置场景中选择“最贪婪”的构造函数或工厂方法的算法相同:在构建时选择具有最大数量可满足依赖关系的变体,类似于容器如何在多个@Autowired构造函数之间进行选择。
1.10.6. 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:
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements 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
BeanNameGeneratorthat defaults to the fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, theFullyQualifiedAnnotationBeanNameGeneratorlocated in packageorg.springframework.context.annotationcan be used for such purposes.
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public 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.
1.10.7. 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:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Scopeannotations are only introspected on the concrete bean class (for annotated components) or the factory method (for@Beanmethods). 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
ScopeMetadataResolverinterface. 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:
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public 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:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
1.10.8. 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:
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Offline
public class CachingMovieCatalog implements 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.
1.10.9. 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
@ComponentScanor<context:component-scan/>directives must remain unchanged to request the context to scan candidates in certain packages. When theApplicationContextdetects 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>5.3.22</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:5.3.22"
}
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:5.3.22"
}
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-indexermust 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.componentsfile 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 settingspring.index.ignoretotrue, either as a JVM system property or via theSpringPropertiesmechanism.

浙公网安备 33010602011771号