Как в Spring правильно создавать и инжектить сервисы?

Разбираюсь со Спрингом, и некоторые вещи мне не до конца ясны. К примеру, прочитанная недавно статья: https://habr.com/ru/post/352954/
В тексте приводится пример создания сервисов:
1. Создаётся интерфейс EmailService:
public interface EmailService {
    void send(String to, String title, String body);
}

2. Реализуя его, создаётся сервис, в котором переопределяется метод:
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class EmailServiceImpl implements EmailService {

    private final JavaMailSender emailSender;

    @Override
    public void send(String to, String subject, String text) {
        MimeMessage message = this.emailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message);
        try {
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(text);
            this.emailSender.send(message);
        } catch (MessagingException messageException) {
            throw new RuntimeException(messageException);
        }
    }
}

А дальше в SchedulerService инжектится не EmailServiceImpl, а "исходный" EmailService:
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SchedulerService {

    private final UserRepositoryService userService; 

    private final EmailService emailService;

}

Вопросы, которые мне не ясны:
1. Это нормально?
2. Почему инжектится "исходный" интерфейс?
3. Зачем создаётся EmailServiceImpl, если он не используется? Он ведь не используется?
4. Тут какая-то хитрая магия Spring? Он видит, что есть ИмяИнтерфейсаImpl и учитывает это в процессе? Как это работает?
5. Как правильно в Spring создавать интерфейсы и правильно их инжектить?
  • Вопрос задан
  • 1569 просмотров
Решения вопроса 2
xez
@xez
TL Junior Roo
1. Да
2. Интерфейс не класс и не бин - его нельзя заинжектить. Там указано, что нужно заинжектить Бин, реализующий интерфейс.
3. EmailServiceImpl как раз и будет использоваться, т.к. он реализует требуемый интерфейс.
4. Назвать сервис можно как угодно. Главное, чтобы он реализовывал нужный интерфейс.
5. См. п2
Ответ написан
azerphoenix
@azerphoenix Куратор тега Spring
Java Software Engineer
1. Это нормально?

Да, нормально. Вообще, наличие сервисного слоя для реализации бизнес-логики необходимо. В сервисном слое вы можете валидировать dto, конвертировать dto <-> model, а также описывать любую бизнес-логику.
2. Почему инжектится "исходный" интерфейс?

Коллега подсказал вам правильно. Инжектится реализация данного интерфейса. Если у интерфейса есть несколько реализаций, то необходимо будет уточнить какая реализация должна быть заинжекчена

Зачем создаётся EmailServiceImpl, если он не используется? Он ведь не используется?

Конечно же используется. Во время работы происходит примерно следующее:
private EmailService emailService = new EmailServiceImpl();


Тут какая-то хитрая магия Spring? Он видит, что есть ИмяИнтерфейсаImpl и учитывает это в процессе? Как это работает?

Как я и сказал выше, если у интерфейса несколько реализаций, то необходимо уточнить название бина, который должен заинжекчен. Ну а если у интерфейса одна реализация, то конечно же она и будет автоматически взята.

Как правильно в Spring создавать интерфейсы и правильно их инжектить?

Собственно говоря, создайте нужный интерфейс. Затем имплементируйте данный интерфейс. В классах (контроллерах или еще где-то) используйте сам интерфейс для создания слабых связей.
А сприн сам "решит" какую реализацию подключать, ну или же укажите название бина, который нужно заинжектить.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы