查看原文
其他

【SFA官方翻译】:Spring中的组件扫描

baeldung SpringForAll社区 2020-10-17

原文链接:https://www.baeldung.com/spring-component-scanning

作者:baeldung

译者:ForeverL888

1. 概览

在本文中,我们来回顾一下Spring中组件的扫描。当我们在工作中使用Spring,我们会在类上使用注解来让这些类成为Spring的 beans。但是,除此之外,我们还可以告诉Spring到哪里去获取这些被注解的类。因为不是所有的使用注解的类,都需要在本次运行中成为Spring中的beans。

当然,Spring提供了默认的组件扫描策略,但是我们同样可以自定义哪些包可以进行扫描。

首先,我们来看一看默认配置。

2. 无参的@ComponentScan

2.1. 在Spring应用中使用@ComponentScan

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

我们在com.baeldung.componentscan.springapp包有一个使用@Configuration注解的类:

  1. @Configuration

  2. @ComponentScan

  3. public class SpringComponentScanApp {

  4.    private static ApplicationContext applicationContext;

  5.    @Bean

  6.    public ExampleBean exampleBean() {

  7.        return new ExampleBean();

  8.    }

  9.    public static void main(String[] args) {

  10.        applicationContext = new AnnotationConfigApplicationContext(SpringComponentScanApp.class);

  11.        for (String beanName : applicationContext.getBeanDefinitionNames()) {

  12.            System.out.println(beanName);

  13.        }

  14.    }

  15. }

另外,我们在 com.baeldung.componentscan.springapp.animals包下有CatDog两个组件:

  1. package com.baeldung.componentscan.springapp.animals;

  2. // ...

  3. @Component

  4. public class Cat {}

  1. package com.baeldung.componentscan.springapp.animals;

  2. // ...

  3. @Component

  4. public class Dog {}

最后,我们在com.baeldung.componentscan.springapp.flowers 包下有Rose组件:

  1. package com.baeldung.componentscan.springapp.flowers;

  2. // ...

  3. @Component

  4. public class Rose {}

main() 方法的输出中会包含com.baeldung.componentscan.springapp 包及其子包下所有的beans:

  1. springComponentScanApp

  2. cat

  3. dog

  4. rose

  5. exampleBean

注意主应用程序类也是一个bean,因为它使用了@Configuration注解,该注解的实质是@Component

同时也要注意的是主应用程序类和配置类不需要一定要放在一起。如果它们在不同的类中,那么把主应用程序类放在哪里是无关紧要的。只有配置类的位置才是至关重要的,因为组件扫描默认是从配置类所在的包及其子包开始的。

最后,注意下,我们示例中的@ComponentSacn等价于

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

其中basePackages 参数是指定用于扫描的包或者是包的数组。

2.2. 在Spring应用中使用@SpringBootApplication

Spring Boot的很多技巧都是隐含的。我们使用@SpringBootApplication 注解,实际上该注解由其它的三个注解组成:

  1. @Configuration

  2. @EnableAutoConfiguration

  3. @ComponentScan

让我们在com.baeldung.componentscan.springbootapp 包下创建一个类型的结构。这时候,我们的主应用程序类是这样子的:

  1. package com.baeldung.componentscan.springbootapp;

  2. // ...

  3. @SpringBootApplication

  4. public class SpringBootComponentScanApp {

  5.    private static ApplicationContext applicationContext;

  6.    @Bean

  7.    public ExampleBean exampleBean() {

  8.        return new ExampleBean();

  9.    }

  10.    public static void main(String[] args) {

  11.        applicationContext = SpringApplication.run(SpringBootComponentScanApp.class, args);

  12.        checkBeansPresence("cat", "dog", "rose", "exampleBean", "springBootComponentScanApp");

  13.    }

  14.    private static void checkBeansPresence(String... beans) {

  15.        for (String beanName : beans) {

  16.            System.out.println("Is " + beanName + " in ApplicationContext: " +

  17.              applicationContext.containsBean(beanName));

  18.        }

  19.    }

  20. }

其它的包和类都保持不变,我们会将它们拷贝到com.baeldung.componentscan.springbootapp 包下。

Spring Boot扫描的包和前面例子非常类似,让我们来看下该例子的输出结果:

  1. Is cat in ApplicationContext: true

  2. Is dog in ApplicationContext: true

  3. Is rose in ApplicationContext: true

  4. Is exampleBean in ApplicationContext: true

  5. Is springBootComponentScanApp in ApplicationContext: true

在第二个示例中,我们不会打印出所有的beans,而是判断这些beans是否存在。这种做法的原因是:如果打印出所有的beans,那么输出结果将会变的非常冗余。

这是因为隐含的@EnableAutoConfiguration 注解会根据pom.xml文件中的依赖自动创建非常多的beans。

3. 使用带参的 @ComponentScan

现在,让我们自定义下可扫描的路径。比如说,我们想要排除扫描Rose这个bean。

我们有多种方式可以实现这一目的。首先我们可以通过改变basePackages参数:

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

  2. @Configuration

  3. public class SpringComponentScanApp {

  4.   // ...

  5. }

现在,我么可以看到输出结果是

  1. springComponentScanApp

  2. cat

  3. dog

  4. exampleBean

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

  • springComponentScanApp 作为一个配置bean被创建,同时它也是作为传递给AnnotationConfigApplicationContext的一个参数

  • exampleBean 是在配置类中的一个内部配置bean

  • cat 和dog 两个bean是被指定的 com.baeldung.componentscan.springapp.animals 包名下的bean

另一种方式是使用一个过滤器,通过指定的正则表达式来过滤掉某些类:

  1. @ComponentScan(excludeFilters =

  2.  @ComponentScan.Filter(type=FilterType.REGEX,

  3.    pattern="com\\.baeldung\\.componentscan\\.springapp\\.flowers\\..*"))

当然,我们也可以使用另外一种过滤类型:

  1. @ComponentScan(excludeFilters =

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

上面所列的自定义的方式同样也是适合于Spring Boot的。我们可以同时使用@ComponentScan@SpringBootApplication注解,并且它产生的效果是一样的:

  1. @SpringBootApplication

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

4. 默认包

我们应当尽量避免将@Configuration注解的类,放置到默认包下(对一个类没有使用任何的包名进行修饰)。在这种情况下,Spring会扫描类路径下的所有的jar中的类文件。这是会产生错误并且导致我们的应用程序可能无法正常启动。

5. 结论

在这个文章中,我们学到了spring默认扫描哪些的包路径,也学到了如何去自定义这些包路径。 和往常一样,完整的示例代码在github上。

推荐: 分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

上一篇:【SFA官方翻译】:Spring集成Java DSL

关注公众号


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存