← Назад к вопросам

На основании чего происходит сканирование с использованием @ComponentScan

2.0 Middle🔥 201 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Сканирование компонентов с @ComponentScan

Что такое @ComponentScan

@ComponentScan — это аннотация в Spring Framework, которая автоматически сканирует классы и находит компоненты Spring (beans) для их регистрации в контексте приложения.

На основании чего происходит сканирование

1. Аннотации на классах

Scan ищет классы, помеченные специальными аннотациями:

// Spring ищет эти аннотации:
@Component      // Базовый компонент
@Service        // Сервис
@Repository     // Репозиторий (DAO)
@Controller     // Контроллер
@RestController // REST контроллер
и другие...

Пример:

// Spring найдёт этот класс через @ComponentScan
@Service
public class UserService {
    public User getUserById(Long id) {
        // реализация
    }
}

@Repository
public class UserRepository {
    public User findById(Long id) {
        // реализация
    }
}

@RestController
public class UserController {
    // контроллер
}

2. Базовый пакет (Base Package)

@ComponentScan сканирует определённый пакет и его подпакеты:

// Сканирует пакет com.example и всё внутри
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

// Или можно указать несколько пакетов
@ComponentScan(basePackages = {"com.example", "com.other"})
public class AppConfig {
}

Структура проекта:

com.example/
├── config/
│   ├── AppConfig.java        ← @ComponentScan тут
│   └── SecurityConfig.java   ← НАЙДЁТСЯ (в подпакете)
├── service/
│   ├── UserService.java      ← НАЙДЁТСЯ (@Service)
│   └── OrderService.java     ← НАЙДЁТСЯ (@Service)
├── repository/
│   ├── UserRepository.java   ← НАЙДЁТСЯ (@Repository)
│   └── OrderRepository.java  ← НАЙДЁТСЯ (@Repository)
└── controller/
    ├── UserController.java   ← НАЙДЁТСЯ (@RestController)
    └── OrderController.java  ← НАЙДЁТСЯ (@RestController)

com.other/
├── external/
│   └── ExternalService.java  ← НЕ найдётся (другой пакет)

3. Класс-ссылка (basePackageClasses)

Можно использовать класс вместо строки:

@ComponentScan(basePackageClasses = UserService.class)
public class AppConfig {
}

// Spring найдёт пакет, где находится UserService
// и будет сканировать оттуда

Это безопаснее, чем строки (рефакторинг не сломает).

4. Фильтры (Filters)

Можно включать или исключать определённые классы:

// Включить только @Service аннотированные классы
@ComponentScan(
    basePackages = "com.example",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        classes = Service.class
    )
)
public class AppConfig {
}

// Исключить классы с определённым паттерном
@ComponentScan(
    basePackages = "com.example",
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.REGEX,
        pattern = ".*Test.*"
    )
)
public class AppConfig {
}

Как работает процесс сканирования

Шаг 1: Определение пакета

@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

// Spring знает: сканировать com.example и подпакеты

Шаг 2: Поиск файлов .class

com/example/**/*.class
  ↓
Найдены все .class файлы в пакете и подпакетах

Шаг 3: Проверка аннотаций

// Для каждого класса проверяется:
if (hasAnnotation(clazz, Component.class) ||
    hasAnnotation(clazz, Service.class) ||
    hasAnnotation(clazz, Repository.class) ||
    hasAnnotation(clazz, Controller.class)) {
    // Это компонент, нужно зарегистрировать
}

Шаг 4: Регистрация в контексте

// Spring создаёт Bean и регистрирует в контексте
BeanDefinition beanDef = new BeanDefinition(clazz);
contextRegistry.registerBeanDefinition("userService", beanDef);

// Теперь можно инъектировать
@Autowired
private UserService userService;  // Найдётся в контексте

Полный пример

Конфигурация

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

Сканируемые компоненты

// com/example/service/UserService.java
@Service
public class UserService {
    public User getUserById(Long id) {
        return new User(id, "John");
    }
}

// com/example/repository/UserRepository.java
@Repository
public class UserRepository {
    public User save(User user) {
        System.out.println("Сохранён: " + user);
        return user;
    }
}

// com/example/controller/UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}

Как это работает

1. Spring запускается
2. Видит @ComponentScan(basePackages = "com.example")
3. Сканирует папку com.example/**
4. Находит UserService (@Service) → регистрирует bean
5. Находит UserRepository (@Repository) → регистрирует bean
6. Находит UserController (@RestController) → регистрирует bean
7. UserController хочет UserService (@Autowired)
8. Spring вводит найденный bean
9. Всё работает!

Когда @ComponentScan нужна

Используется в Java конфигурации

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // В этом классе можно определять дополнительные beans
    
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

Spring Boot автоматически

@SpringBootApplication  // Включает @ComponentScan автоматически
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// @SpringBootApplication эквивалентен:
// @Configuration + @EnableAutoConfiguration + @ComponentScan

Примеры различных фильтров

Только сервисы

@ComponentScan(
    basePackages = "com.example",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        classes = Service.class
    ),
    useDefaultFilters = false  // отключить остальные
)
public class ServiceOnlyConfig {
}

// Найдёт: UserService, OrderService
// НЕ найдёт: UserController, UserRepository

Исключить Test классы

@ComponentScan(
    basePackages = "com.example",
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.REGEX,
        pattern = ".*Test.*"
    )
)
public class ProdConfig {
}

// НЕ найдёт: UserServiceTest, UserRepositoryTest

По названию (ASPECTJ)

@ComponentScan(
    basePackages = "com.example",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ASPECTJ,
        pattern = "com.example.service.*"
    )
)
public class ServicePackageConfig {
}

// Найдёт только классы в пакете com.example.service

Порядок сканирования

1. @ComponentScan -> находит пакет (basePackages)
2. ClassPathScanningCandidateComponentProvider скарирует файлы
3. Для каждого .class файла:
   a) Проверяет аннотации (@Component, @Service и т.д.)
   b) Применяет фильтры (includeFilters, excludeFilters)
   c) Если прошёл проверку → регистрирует BeanDefinition
4. Spring создаёт beans и инъектирует зависимости

Выводы

@ComponentScan сканирует на основании:

  1. Аннотаций (@Service, @Repository, @Controller и т.д.)
  2. Пакета (basePackages, basePackageClasses)
  3. Фильтров (includeFilters, excludeFilters)
  4. Типа фильтра (ANNOTATION, REGEX, ASPECTJ, CUSTOM)

Это основной механизм Spring для автоматической регистрации beans без явного определения каждого компонента. В Spring Boot @ComponentScan включена автоматически через @SpringBootApplication.