Avatar ·

В чем разница между @Component, @Repository и @Service в Spring?

Привет! Я начал изучать Spring и пытаюсь разобраться с аннотациями стереотипов. Вроде бы все три (@Component, @Repository, @Service) делают одно и то же — регистрируют бин в контексте Spring. Но зачем тогда их три? Я написал простой сервис для работы с пользователями. Вот мой код: ```java @Component public class UserService { public void saveUser(User user) { System.out.println("User saved: " + user.getName()); } } @Repository public class UserRepository { public void saveToDatabase(User user) { // тут работа с БД } } @Service public class UserManager { public void processUser(User user) { // бизнес-логика } } ``` Все три работают, я могу внедрить любой из них через @Autowired. В чем тогда принципиальная разница? Может быть, это просто разные названия для одного и того же? Или есть какая-то магия, которую я упускаю? Видел на Stack Overflow много обсуждений, но хочется получить четкое объяснение на русском с примерами. Заранее спасибо за помощь! <<>>

Основное различие: семантика и дополнительные возможности

Вы правы, технически все три аннотации регистрируют бин в контексте Spring. Но разница есть, и она важна для правильной архитектуры приложения. Давайте разберем каждую аннотацию подробно.

1. @Component — базовый стереотип

Это самая общая аннотация. Используйте её для классов, которые не подходят под категории сервиса или репозитория. Например, утилиты, хелперы, конвертеры:

@Component
public class EmailValidator {
    public boolean isValid(String email) {
        return email != null && email.contains("@");
    }
}

2. @Repository — работа с данными

Эта аннотация предназначена для DAO (Data Access Object) классов, которые работают с базой данных. Главная особенность — автоматическое преобразование исключений. Spring перехватывает специфичные для БД исключения (например, SQLException) и преобразует их в DataAccessException — единое иерархическое исключение Spring.

@Repository
public class UserRepository {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        try {
            return jdbcTemplate.queryForObject(
                "SELECT * FROM users WHERE id = ?",
                new BeanPropertyRowMapper<>(User.class),
                id
            );
        } catch (DataAccessException e) {
            // Spring уже преобразовал SQLException в DataAccessException
            throw new UserNotFoundException("User not found with id: " + id, e);
        }
    }
}

Без @Repository вам пришлось бы вручную обрабатывать SQLException и преобразовывать его в своё исключение.

3. @Service — бизнес-логика

Аннотация для сервисного слоя. Она не добавляет никакого дополнительного поведения (как @Repository), но служит важным маркером для разработчиков и инструментов анализа кода. @Service явно указывает, что класс содержит бизнес-логику приложения.

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private EmailService emailService;
    
    public void registerUser(User user) {
        // 1. Валидация
        if (user.getEmail() == null || user.getName() == null) {
            throw new IllegalArgumentException("User data is incomplete");
        }
        
        // 2. Сохранение в БД
        userRepository.save(user);
        
        // 3. Отправка приветственного письма
        emailService.sendWelcomeEmail(user.getEmail());
        
        // 4. Логирование
        log.info("User registered: {}", user.getEmail());
    }
}

Практические рекомендации

  • @Component — для общих компонентов, которые не являются сервисами или репозиториями
  • @Repository — для классов, работающих с БД (DAO, репозитории)
  • @Service — для классов с бизнес-логикой (сервисы)

Важное замечание про AOP

Spring AOP (аспектно-ориентированное программирование) часто использует эти аннотации как точки среза (pointcut). Например, вы можете настроить логирование для всех @Service классов или мониторинг производительности для всех @Repository классов. Если бы вы использовали везде @Component, такая настройка была бы менее гибкой.

Техническая реализация

Если заглянуть под капот, то @Service и @Repository являются специализированными версиями @Component:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  // Вот ключевая строка!
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

То есть @Service — это просто @Component с другим именем, но с важной семантической нагрузкой.

Итог

Используйте правильные аннотации не потому, что Spring заставит, а чтобы ваш код был понятным и поддерживаемым. Через месяц вы или ваш коллега, глядя на @Service, сразу поймете, что это слой бизнес-логики, а @Repository — работа с данными. Это дисциплина и хороший тон в Spring-разработке.

<<>>

Дополнение: как это работает на практике с компонентным сканированием

Коллега выше отлично описал семантику. Я добавлю важный технический нюанс, который часто упускают новички.

Все три аннотации обрабатываются одним и тем же механизмом — ClassPathScanningCandidateComponentProvider. Spring сканирует классы в указанных пакетах и ищет аннотации, помеченные @Component (включая @Service, @Repository, @Controller).

Но есть хитрость: если вы используете Spring Boot и аннотацию @SpringBootApplication, то автоматически включается сканирование компонентов в подпакетах. И тут важно помнить про фильтры. Вы можете настроить сканирование только определённых аннотаций:

@Configuration
@ComponentScan(
    basePackages = "com.example",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        classes = Service.class
    ),
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        classes = Repository.class
    )
)
public class AppConfig {
    // Будут найдены только @Service, @Repository будет проигнорирован
}

Также стоит отметить, что @Repository — единственная из трёх, которая добавляет поведение через AOP (преобразование исключений). @Service и @Component — это просто маркеры.

Мой совет: не зацикливайтесь на технической разнице — она минимальна. Сосредоточьтесь на том, чтобы ваш код был читаемым и следовал общепринятым соглашениям. Это окупится, когда проект вырастет и в нём будет 500+ классов.