深入分析 @ComponentScan 在 Spring 中的应用

0 阅读3分钟

@ComponentScan 是 Spring 框架中非常核心的一个注解,作用是自动扫描指定包路径中的组件类,并将其注册为 Spring 容器的 Bean。这大大简化了 Spring 配置和依赖注入,避免了我们手动编写冗长的 Bean 配置代码。在这篇文章中,我们通过一些实际代码来分析 @ComponentScan 的工作原理及应用。

301.png

代码分析

我们从以下代码入手,逐步解析 @ComponentScan 的工作过程:

配置类 AppConfig

package org.spring.mastery.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class AppConfig {
}

这段代码定义了一个 Spring 配置类 AppConfig,它通过 @Configuration 注解标记为配置类,同时使用 @ComponentScan 注解指定扫描当前包及其子包中的所有符合条件的组件类。@ComponentScan 默认扫描的包路径是当前类所在的包及其子包,即 org.spring.mastery 及其下的子包。该配置类会告诉 Spring 容器要扫描哪些类,并注册为 Bean。

服务类 MyService

package org.spring.mastery.service;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class MyService {

    @PostConstruct
    public void init() {
        System.out.println("MyService initialized");
    }
}

MyService 类被 @Service 注解标记为一个服务类,@Service@Component 的一种特化形式。@PostConstruct 注解的 init() 方法会在 MyService Bean 被初始化后自动调用。在 Spring 启动时,MyService 会被 @ComponentScan 自动扫描到,并在 Spring 容器中注册为一个 Bean。初始化时,init() 方法会输出 "MyService initialized"

仓库类 MyRepository

package org.spring.mastery.repository;

import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;

@Repository
public class MyRepository {

    @PostConstruct
    public void init() {
        System.out.println("MyRepository initialized");
    }
}

MyRepository 类被 @Repository 注解标记为仓库类,与 @Service 类似,@Repository 也是 @Component 的特化注解。@PostConstruct 注解标记的 init() 方法会在 MyRepository Bean 被初始化时自动调用,输出 "MyRepository initialized"

排除的服务类 ExcludedService

package org.spring.another;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class ExcludedService {

    @PostConstruct
    public void init() {
        System.out.println("ExcludedService initialized");
    }
}

ExcludedService 类也是一个标准的 Spring 组件类,使用了 @Service 注解。由于我们在 AppConfig 中没有明确排除扫描路径,它同样会被 @ComponentScan 扫描到并注册为 Bean。不过,假设我们要排除这个类,可以通过配置 excludeFilters 来做到。

扩展:如何排除特定组件

如果我们不希望某些类被 @ComponentScan 扫描到,可以使用 excludeFilters 属性进行排除。例如,假设我们不希望 ExcludedService 被扫描到:

@Configuration
@ComponentScan(basePackages = "org.spring.mastery", excludeFilters = @ComponentScan.Filter(Service.class))
public class AppConfig {
}

这里,excludeFilters 配置排除了所有标记为 @Service 的类,因此 ExcludedService 将不会被扫描到。

@ComponentScan 扫描机制解析

  1. 扫描路径
    @ComponentScan 默认会扫描当前类所在的包及其子包。在 AppConfig 类中,由于 AppConfig 位于 org.spring.mastery.config 包下,所以默认会扫描 org.spring.mastery.config 及其子包(如 org.spring.mastery.serviceorg.spring.mastery.repository)中的所有类。
  2. 注解识别
    @ComponentScan 会识别 @Component 及其衍生注解(如 @Service@Repository@Controller)标注的类,并将这些类注册到 Spring 容器中。@PostConstruct 注解的初始化方法会在 Bean 被创建后自动执行。
  3. 优先级和覆盖
    如果存在多个 @ComponentScan 配置,Spring 会按扫描顺序来加载和注册 Bean。如果有相同的 Bean 被多次扫描到,Spring 会根据配置的顺序决定哪个 Bean 会被注册,后加载的 Bean 会覆盖前加载的 Bean。为了避免冲突,通常建议明确指定扫描路径和排除不必要的类。
  4. 核心调用链路(一条线看懂)

100.png

总结

101.png @ComponentScan是 Spring 中用于自动扫描组件并注册 Bean 的关键注解。它极大地简化了 Spring 配置,避免了手动配置每个 Bean。通过合理配置basePackagesexcludeFilters,可以灵活控制扫描的范围和排除不必要的类。在实际应用中,@ComponentScan` 提供了一种清晰、简便的方式来管理 Spring 容器中的组件,同时也为开发者提供了更多的配置灵活性。