Spring Component Scanning – Spring组件扫描

最后修改: 2018年 8月 21日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll cover component scanning in Spring. When working with Spring, we can annotate our classes in order to make them into Spring beans. Furthermore, we can tell Spring where to search for these annotated classes, as not all of them must become beans in this particular run.

在本教程中,我们将介绍Spring中的组件扫描。在使用Spring时,我们可以对我们的类进行注解,以便将它们变成Spring Bean。此外,我们可以告诉Spring在哪里搜索这些注解的类,因为在这个特定的运行中,并非所有的类都必须成为Bean。

Of course, there are some defaults for component scanning, but we can also customize the packages for search.

当然,有一些默认的组件扫描,但我们也可以自定义搜索的包。

First, let’s look at the default settings.

首先,让我们看一下默认设置。

2. @ComponentScan Without Arguments

2.@ComponentScan没有参数

2.1. Using @ComponentScan in a Spring Application

2.1.在Spring应用程序中使用@ComponentScan

With Spring, we use the @ComponentScan annotation along with the @Configuration annotation to specify the packages that we want to be scanned. @ComponentScan without arguments tells Spring to scan the current package and all of its sub-packages.

在Spring中,我们使用@ComponentScan注解和@Configuration注解来指定我们想要扫描的包@ComponentScan没有参数,告诉Spring扫描当前包和其所有子包。

Let’s say we have the following @Configuration in com.baeldung.componentscan.springapp package:

假设我们在com.baeldung.componentcan.springapp包里有以下@Configuration

@Configuration
@ComponentScan
public class SpringComponentScanApp {
    private static ApplicationContext applicationContext;

    @Bean
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    public static void main(String[] args) {
        applicationContext = 
          new AnnotationConfigApplicationContext(SpringComponentScanApp.class);

        for (String beanName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanName);
        }
    }
}

In addition, we have the Cat and Dog components in com.baeldung.componentscan.springapp.animals package:

此外,我们在com.baeldung.componentcan.springapp.animals包里有CatDog组件。

package com.baeldung.componentscan.springapp.animals;
// ...
@Component
public class Cat {}
package com.baeldung.componentscan.springapp.animals;
// ...
@Component
public class Dog {}

Finally, we have the Rose component in com.baeldung.componentscan.springapp.flowers package:

最后,我们在com.baeldung.componentcan.springapp.flower包中有一个Rose组件。

package com.baeldung.componentscan.springapp.flowers;
// ...
@Component
public class Rose {}

The output of the main() method will contain all the beans of com.baeldung.componentscan.springapp package and its sub-packages:

main()方法的输出将包含com.baeldung.componentcan.springapp包及其子包中的所有bean。

springComponentScanApp
cat
dog
rose
exampleBean

Note that the main application class is also a bean, as it’s annotated with @Configuration, which is a @Component.

请注意,主应用程序类也是一个Bean,因为它被注解为@Configuration,,它是一个@Component

We should also note that the main application class and the configuration class are not necessarily the same. If they are different, it doesn’t matter where we put the main application class. Only the location of the configuration class matters, as component scanning starts from its package by default.

我们还应该注意,主应用程序类和配置类不一定相同。如果它们是不同的,我们把主应用类放在哪里并不重要。只有配置类的位置是重要的,因为组件扫描默认从它的包开始

Finally, note that in our example, @ComponentScan is equivalent to:

最后,请注意,在我们的例子中,@ComponentScan等同于。

@ComponentScan(basePackages = "com.baeldung.componentscan.springapp")

The basePackages argument is a package or an array of packages for scanning.

参数 basePackages是一个包或一个用于扫描的包阵列。

2.2. Using @ComponentScan in a Spring Boot Application

2.2.在Spring Boot应用程序中使用@ComponentScan

The trick with Spring Boot is that many things happen implicitly. We use the @SpringBootApplication annotation, but it’s a combination of three annotations:

Spring Boot的诀窍在于,许多事情都是隐性发生的。我们使用@SpringBootApplication注解,但它是三个注解的组合。

@Configuration
@EnableAutoConfiguration
@ComponentScan

Let’s create a similar structure in com.baeldung.componentscan.springbootapp package. This time the main application will be:

让我们在com.baeldung.componentcan.springbootapp包中创建一个类似的结构。这一次,主要的应用程序将是。

package com.baeldung.componentscan.springbootapp;
// ...
@SpringBootApplication
public class SpringBootComponentScanApp {
    private static ApplicationContext applicationContext;

    @Bean
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    public static void main(String[] args) {
        applicationContext = SpringApplication.run(SpringBootComponentScanApp.class, args);
        checkBeansPresence(
          "cat", "dog", "rose", "exampleBean", "springBootComponentScanApp");

    }

    private static void checkBeansPresence(String... beans) {
        for (String beanName : beans) {
            System.out.println("Is " + beanName + " in ApplicationContext: " + 
              applicationContext.containsBean(beanName));
        }
    }
}

All other packages and classes remain the same, we’ll just copy them to the nearby com.baeldung.componentscan.springbootapp package.

所有其他的包和类都保持不变,我们只是把它们复制到附近的com.baeldung.componentcan.springbootapp包。

Spring Boot scans packages similarly to our previous example. Let’s check the output:

Spring Boot扫描包的方式与我们之前的例子类似。让我们检查一下输出。

Is cat in ApplicationContext: true
Is dog in ApplicationContext: true
Is rose in ApplicationContext: true
Is exampleBean in ApplicationContext: true
Is springBootComponentScanApp in ApplicationContext: true

The reason we’re just checking the beans for existence in our second example (as opposed to printing out all the beans), is that the output would be too large.

在我们的第二个例子中,我们只是检查Bean是否存在(而不是打印出所有的Bean),原因是输出会太大。

This is because of the implicit @EnableAutoConfiguration annotation, which makes Spring Boot create many beans automatically, relying on the dependencies in pom.xml file.

这是因为隐含的@EnableAutoConfiguration注解,它使Spring Boot依靠pom.xml文件中的依赖关系自动创建许多Bean。

3. @ComponentScan With Arguments

3.@ComponentScan With Arguments

Now let’s customize the paths for scanning. For example, let’s say we want to exclude the Rose bean.

现在让我们来定制扫描的路径。例如,假设我们想排除RoseBean。

3.1. @ComponentScan for Specific Packages

3.1.@ComponentScan用于特定软件包

We can do this a few different ways. First, we can change the base package:

我们可以用几种不同的方法来做这件事。首先,我们可以改变基本包。

@ComponentScan(basePackages = "com.baeldung.componentscan.springapp.animals")
@Configuration
public class SpringComponentScanApp {
   // ...
}

Now the output will be:

现在,输出将是。

springComponentScanApp
cat
dog
exampleBean

Let’s see what’s behind this:

让我们看看这背后是什么。

  • springComponentScanApp is created as it’s a configuration passed as an argument to the AnnotationConfigApplicationContext
  • exampleBean is a bean configured inside the configuration
  • cat and dog are in the specified com.baeldung.componentscan.springapp.animals package

All of the above-listed customizations are applicable in Spring Boot too. We can use @ComponentScan together with @SpringBootApplication and the result will be the same:

以上列举的所有定制也适用于Spring Boot。我们可以将@ComponentScan@SpringBootApplication一起使用,结果是一样的。

@SpringBootApplication
@ComponentScan(basePackages = "com.baeldung.componentscan.springbootapp.animals")

3.2. @ComponentScan with Multiple Packages

3.2.@ComponentScan与多个包的关系

Spring provides a convenient way to specify multiple package names. To do so, we need to use a string array.

Spring提供了一种方便的方法来指定多个包名。要做到这一点,我们需要使用一个字符串数组。

Each string of the array denotes a package name:

数组中的每个字符串表示一个包的名称。

@ComponentScan(basePackages = {"com.baeldung.componentscan.springapp.animals", "com.baeldung.componentscan.springapp.flowers"})

Alternatively, since spring 4.1.1, we can use a comma, a semicolon, or a space to separate the packages list:

另外,从spring 4.1.1开始,我们可以使用逗号、分号或空格来分隔软件包列表

@ComponentScan(basePackages = "com.baeldung.componentscan.springapp.animals;com.baeldung.componentscan.springapp.flowers")
@ComponentScan(basePackages = "com.baeldung.componentscan.springapp.animals,com.baeldung.componentscan.springapp.flowers")
@ComponentScan(basePackages = "com.baeldung.componentscan.springapp.animals com.baeldung.componentscan.springapp.flowers")

3.3. @ComponentScan with Exclusions

3.3.@ComponentScan与排除项

Another way is to use a filter, specifying the pattern for the classes to exclude:

另一种方法是使用一个过滤器,指定要排除的类的模式。

@ComponentScan(excludeFilters = 
  @ComponentScan.Filter(type=FilterType.REGEX,
    pattern="com\\.baeldung\\.componentscan\\.springapp\\.flowers\\..*"))

We can also choose a different filter type, as the annotation supports several flexible options for filtering the scanned classes:

我们也可以选择不同的过滤器类型,因为注解支持几个灵活的选项来过滤扫描的类

@ComponentScan(excludeFilters = 
  @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Rose.class))

4. The Default Package

4.默认包

We should avoid putting the @Configuration class in the default package (i.e. by not specifying the package at all). If we do, Spring scans all the classes in all jars in a classpath, which causes errors and the application probably doesn’t start.

我们应该避免将@Configuration放在默认包中(即完全不指定包)。如果我们这样做,Spring就会扫描classpath中所有jar中的所有类,这会导致错误,而且应用程序可能无法启动。

5. Conclusion

5.结论

In this article, we learned which packages Spring scans by default and how to customize these paths.

在这篇文章中,我们学习了Spring默认扫描的软件包以及如何定制这些路径。

As usual, the complete code is available over on GitHub.

像往常一样,完整的代码可以在GitHub上找到