Spring Core之 Classpath Scanning and Managed Components(类路径扫描和组件管理)


生词汇总:

  • fulfill -------------------------------------- 履行, 满足, 实现,完成
  • state -------------------------------------- 声明, 陈述, 州,国家
  • eligible ------------------------------------ 有资格的
  • brevity ------------------------------------- 简洁,简短, 短暂

一、preface(前言)

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.

本部分内容讲述了如何隐式发现候选者组件的方法之一,即通过类路径扫描。候选者组件指的是那些匹配过滤规则,并且有一个正确的bean定义信息将其注册到容器中的类。这移除了必须使用XML方式注册bean定义的旧状,取而代之的是,你可以使用注解(例如@Component),AspectJ类型表达式,或者你自定义的过滤规则来选择哪些类能够拥有bean定义并注册到容器中。

二、@Component and Further Stereotype Annotations(@Component和其他原型注解)

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). 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.

@Repository 注释是一个用于任何满足存储库角色(也被称为数据访问对象或DAO)或原型类的标记。Spring提供了更多的原型注解:@Component,@Service和@Controller。@Component对Spring管理的组件时通用的。@Repository,@Service和@Controller相对与@Component有更加具体的时用场景(分别用于持久层,服务层和表示层)。因此,你可以对你的组件类使用@Component,但是使用@Repository,@Service或者@Controller替代,你的类能够在各个方面被更恰当的处理。例如,这些原型注解是理想的切入点。@Repository、@Service 和 @Controller 还可以在 Spring Framework 的未来版本中携带额外的语义。因此,如果你在service层考虑使用@Component还是@Service,@Service明显是更好的选择。类似的,正如前面所述,@Repository 已经被支持作为持久层中自动异常转换的标记。

三、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, The Component causes @Service to be treated in the same way as @Component.as the following example shows:

Spring提供的许多注解可以在你的代码中作为元注解使用。一个元注解就是能够应用在其他注解上的注解。例如,前面提到的@Service注解的元注解@Component,Component 使@Service 以与@Component 相同的方式被处理。如下例所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

    // ...
}

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:

你也可以将元注解组合使用来构成组合注解。例如,SpringMVC中的@RestCOntroller注解就是@Controller和@ResponseBody组合而成。除此之外,组合注解可以选择性地从元注解重新声明属性以允许自定义。这在你只想暴露元注解的部分属性时尤其有用。例如,@SessionScope注解将bean的作用域硬编码为session,但是仍然运行自定义端口模式。下面的代码展示了SessionScope注解是如何定义的:

@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;

}

四、Automatically Detecting Classes and Registering Bean Definitions(自动探查类并且注册Bean定义)

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:

Spring能够自动探查出原型类并注册正确的BeanDefinition实例到ApplicationContext。例如,以下两个类满足自动扫描。

@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.)

为了自动扫描和注册正确的bean,你需要添加@ComponentScan到你使用了@Configuration的配置类,其中 basePackages 属性是上述两个类的公共父包。(或者,你可以使用逗号或分号或空格分隔的列表指定包含每个类的父包。)

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    // ...
}

The following alternative uses XML:

在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>

For brevity, the preceding example could have used the value attribute of the annotation (that is, @ComponentScan(“org.example”)).

为了简洁,前面的例子也可以使用该注解的value属性来指定扫描包(即@ComponentScan("org.example)

五、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:

默认情况下,使用了@Component、@Repository、@Service、@Controller、@Configuration 注解的类或本身用@Component 注解的自定义注解是唯一检测到的候选组件。但是,你可以通过应用自定义过滤器来修改和扩展此行为。添加它们作为 @ComponentScan 注解的 includeFilters 或 excludeFilters 属性(或作为 XML 配置中 <context:component-scan> 元素的 <context:include-filter /> 或 <context:exclude-filter /> 子元素)。每个过滤器元素都需要 type 和 expression 属性。下表描述了过滤选项:

Filter TypeExample ExpressionDescription
annotation (default)org.example.SomeAnnotationAn annotation to be present or meta-present at the type level in target components.
assignableorg.example.SomeClassA class (or interface) that the target components are assignable to (extend or implement).
aspectjorg.example…*Service+An AspectJ type expression to be matched by the target components.
regexorg.example.Default.*A regex expression to be matched by the target components’ class names.
customorg.example.MyTypeFilterA custom implementation of the org.springframework.core.type.TypeFilter interface.

The following example shows the configuration ignoring all @Repository annotations and using “stub” repositories instead:

以下示例显示了忽略所有 @Repository 注释并使用“stub”存储库的配置:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

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.

你也可以通过在注解上设置 useDefaultFilters=false 或通过提供 use-default-filters=“false” 作为 <component-scan/> 元素的属性来禁用默认过滤器。这将禁用自动扫描那些使用了@Component,@Service,@Repository,@Controller,@RestController 或@Configuration注解的类。

六、Defining Bean Metadata within Components(在组件内部定义bean元数据)

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:

Spring组件也能够定义bean的元数据到容器,你可以在使用了@Configuration的类中通过@Bean注解来进行元数据的定义,如下例所示:

@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.

上例中的类是一个Spring组件,在它的doWork()方法中有特定于应用程序的代码,但是,它也通过publicInstance()方法提供了一个bean定义,@Bean注解标识了工厂方法和其他bean定义属性,例如通过@Qualifier注解标识限定符,或者其他方法级别的注解@Scope、@Lazy和自定义的限定符注解。

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:

正如前面讨论的那样,支持自动装配字段和方法,并额外支持 @Bean 方法的自动装配。以下示例显示了如何执行此操作:

@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 @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.

@Bean注解标注的方法在常规的Spring组件中与在@Configuration标注的类中有所不同。不同之处在于使用@Component的类中没有使用CGLIB对方法和字段进行拦截增强。CGLIB 代理是调用@Configuration 类中@Bean 方法中的方法或字段的一种方式,通过该方法创建对协作对象的bean 元数据引用。这些方法不是用普通的 Java 语义调用的,而是通过容器来提供 Spring bean 的常规的生命周期管理和代理,尽管通过程序调用@Bean标注的方法来引用其他bean。相反,在普通 @Component 类中调用 @Bean 方法中的方法或字段具有标准的 Java 语义,没有特殊的 CGLIB 处理或其他约束应用。

Calls to static @Bean methods never get intercepted by the container, not even within @Configuration classes (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 @Bean method has standard Java semantics, resulting in an independent instance being returned straight from the factory method itself.

调用@Bean标注的静态方法永远不会被容器拦截,不管是否在@Configuration标注的类中还是之前提到的其他情况,由于技术限制:CGLIB子类只能覆盖非静态方法。因此,直接调用@Bean标注的静态方法和java常规语义是一样的,会将不同的实例作为结果返回。

七、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:

当一个组件被自动扫描出来时,它的bean名称通过BeanNameGenerator的策略产生。默认情况下,任何Spring构造型注解(@Component,@Repository,@Service和@Controller)都包含一个name值,以便在bean定义中提供相应的名称。如果某个注解没有被提供name值,默认的name值为非限定类名小写开头,例如,如果一下组件类会被扫描,它们的名称将会是myMovieLister 和 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.

如果你不想依靠默认的bean名称生成策略,你可以自定义一个命名策略。首先,实现BeanNameGenerator接口,并且确保提供一个无参构造函数。然后,在配置扫描器时提供全限定类名,如下例所示:

@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>

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.

如果多个组件类有相同的非全限定类名导致自动名称生成冲突(例如,不同包下相同名称的类),你可能就需要配置一个通过全限定类名来生成名称的BeanNameGenerator 。从Spring5.2.3开始,org.springframework.context.annotation包中的FullyQualifiedAnnotationBeanNameGenerator 可以用来达到此目的。

八、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:

和Spring管理的常规组件一样,主动扫描组件的默认作用域为单例。但是,有时候你需要一个不同的作用域的话可以通过@Scope注解指定。你可以在该注解中提供一个name值,如下所示:

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

@Scope annotations are only introspected on the concrete bean class (for annotated components) or the factory method (for @Bean methods).

@Scope 注解仅在具体 bean 类(带注解的组件)或工厂方法(对于 @Bean 方法)上进行内省。也就是说需和@Bean、@Component等注解结合使用。

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:

为了提供一个自定义的作用域生成策略而不是依赖基于注解方法,你可以实现ScopeMetadataResolver 接口,并确保实现类中含有无参构造函数。然后在配置组件扫描器时提供全限定类名,如下例所示:

@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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值