Зарегистрируйтесь, чтобы продолжить обучение

Инверсия зависимостей Java: Корпоративные приложения на Spring Boot

Spring Boot — это проект, который построен поверх Spring Framework — платформы для создания корпоративных приложений, куда входят не только веб-сервисы. У Spring Framework много возможностей, но ядро во всем этом многообразии — это контейнер зависимостей.

В этом уроке мы изучим, как Spring собирает приложение воедино с помощью инверсии зависимостей.

Допустим, у нас есть репозиторий UserRepository для интеграции модели User с базой данных. В простейшем случае репозиторий выглядит так:

package io.hexlet.spring.repository;

import hexlet.code.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
// Во время компиляции этот интерфейс превращается в конкретный класс
public interface UserRepository extends JpaRepository<User, Long> {
}

Теперь представим, что на основе этого интерфейса Spring генерирует класс с именем UserRepositoryImpl. Как использовать этот класс в своем коде — например, в методе создания пользователя?

@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public User create(@RequestBody User user) {
    // Как использовать репозиторий?
}

Если работать в лоб, то получится такой код:

var repository = new UserRepositoryImpl();
repository.save(user);

Этот код не сработает, потому что у репозитория нет доступа к соединению с базой данных — он не может сохранить данные. Правильный код должен выглядеть как-то так:

// Соединяемся с базой данных
var conn = DriverManager.getConnection(/* параметры соединения */);
var repository = new UserRepositoryImpl(conn); // Передаем соединение в репозиторий

Придется дублировать этот код везде, потому что репозиторий нужен буквально в каждом контроллере. Но даже если мы вынесем его в метод, мы все равно наткнемся на проблему создания объектов на каждый запрос. С репозиторием это допустимо, но с базой данных соединяться на каждый запрос нельзя — это создаст большую нагрузку на систему.

Пример выше показывает, как возникают зависимости в коде. Так происходит, когда одни классы используют объекты других классов. Причем здесь мы рассмотрели учебный пример с всего одной зависимостью.

В реальных проектах зависимостей может быть значительно больше. Кроме того, они могут быть гораздо глубже — тогда класс может зависеть от класса, который зависит от другого класса, а тот в свою очередь еще от одного класса. Посмотрите, во что превратился бы код:

var obj1 = new OneClass();
var obj2 = new TwoClass(obj1);
var obj3 = new ThreeClass();
var obj4 = new FourClass(obj2, obj3);

Spring Framework решает эту задачу автоматически с помощью инверсии зависимостей. Программист указывает, как одни классы зависят от других, а фреймворк сам собирает нужные объекты и передает их в код для использования. В таких ситуациях Spring:

  • Находит все классы, помеченные его аннотациями
  • Вычисляет, как классы зависят друг от друга
  • Собирает нужные объекты
  • Подставляет их при инициализации объектов этих классов

Эта схема работает только с классами, объекты которых Spring создает сам. Иначе ничего подставить не получится. Например, модели не попадают под эту схему:

// Мы работаем с классом напрямую
// Поэтому подстановка зависимостей не сработает
var user = new User();

То же самое относится к любым другим классам, объекты которых мы создаем самостоятельно.

Инъекции

Подстановка зависимых объектов в другие объекты называется инъекцией. Ниже мы рассмотрим, как это делается в Spring Boot.

Инъекция через конструктор

Классический способ выполнить подстановку зависимостей — это инъекция через конструктор. Изучим пример с контроллером:

@RestController
@RequestMapping("/users")
public class UserController {

    private UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // Где-то здесь в методах используем userRepository
}

Spring анализирует конструкторы своих компонентов. Если он видит там указанные зависимости, то использует эти конструкторы для создания объектов и внедрения зависимостей.

Инъекция с помощью аннотации @Autowired

Более простой и широко используемый способ инъекции — это аннотация @Autowired:

import org.springframework.beans.factory.annotation.Autowired;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;
}

Это самый короткий и удобный способ указывать зависимости. В большинстве случаев этого подхода достаточно.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»