Основы разработки на Spring Boot

Интенсивный курс по созданию современных Java-приложений с использованием фреймворка Spring Boot. Вы пройдете путь от настройки окружения до развертывания готового REST API с базой данных и безопасностью.

1. Введение в экосистему Spring Boot: архитектура, стартеры и создание первого проекта

Введение в экосистему Spring Boot: архитектура, стартеры и создание первого проекта

Добро пожаловать в курс «Основы разработки на Spring Boot». Если вы читаете эту статью, значит, вы уже знакомы с языком Java и, вероятно, слышали о том, что создание корпоративных приложений может быть сложным и трудоемким процессом. Долгое время Java-разработчики тратили часы, а иногда и дни, на настройку окружения, написание конфигурационных XML-файлов и разрешение конфликтов версий библиотек, прежде чем написать хотя бы одну строчку бизнес-логики.

Spring Boot появился как ответ на эту проблему. Это инструмент, который изменил правила игры в мире Java-разработки, сделав процесс создания приложений быстрым, предсказуемым и даже приятным. В этой первой статье мы разберем, что такое Spring Boot, на каких «китах» он стоит, и создадим наше первое работающее веб-приложение.

Что такое Spring Boot и зачем он нужен?

Чтобы понять суть Spring Boot, нужно сначала вспомнить о его «старшем брате» — Spring Framework. Spring Framework — это мощнейший каркас для создания Java-приложений, предоставляющий огромный набор инструментов: от внедрения зависимостей (Dependency Injection) до безопасности и работы с базами данных.

Однако у Spring Framework есть особенность: он очень гибкий. Эта гибкость требует тщательной настройки. Представьте, что вам подарили конструктор LEGO, в котором 5000 деталей, но нет инструкции. Вы можете собрать всё что угодно, но вам придется потратить много времени на подбор нужных деталей.

Spring Boot — это тот же набор деталей, но уже частично собранный в готовые модули (машину, дом, самолет) с понятной инструкцией. Он берет на себя рутинную работу по настройке Spring Framework, позволяя вам сосредоточиться на коде вашего приложения.

!Диаграмма, показывающая три основных столпа Spring Boot: Стартеры, Автоконфигурацию и Встроенный сервер.

Основные цели Spring Boot:

  • Упростить управление зависимостями (библиотеками).
  • Автоматизировать конфигурацию приложения.
  • Предоставить встроенный веб-сервер, чтобы приложение можно было запустить как обычный Java-файл.
  • Архитектура: Три кита Spring Boot

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

    1. Стартеры (Starters)

    В традиционной Java-разработке, если вы хотите создать веб-приложение, вам нужно вручную добавить в проект множество библиотек: для обработки HTTP-запросов, для работы с JSON, для валидации данных, для логирования и так далее. При этом нужно следить, чтобы версии этих библиотек были совместимы друг с другом.

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

    Рассмотрим пример. Если вы хотите создать веб-сервис, вам достаточно добавить в проект всего одну зависимость — spring-boot-starter-web. Она автоматически «подтянет» за собой:

    * Spring MVC (для обработки веб-запросов); * Jackson (для работы с JSON); * Tomcat (веб-сервер); * Logback (для логирования).

    Сравним подход без стартеров и с ними:

    | Задача | Традиционный подход (Spring Framework) | Подход Spring Boot | | :--- | :--- | :--- | | Зависимости | Нужно найти и прописать 5-10 разных библиотек в pom.xml или build.gradle. | Достаточно одной строки: spring-boot-starter-web. | | Версии | Нужно вручную проверять совместимость версий библиотек. | Spring Boot сам управляет версиями, гарантируя их совместимость. | | Конфигурация | Требуется явная настройка бинов (компонентов) для каждой библиотеки. | Базовая настройка уже включена. |

    2. Автоконфигурация (Auto-configuration)

    Это, пожалуй, самая «магическая» часть Spring Boot. Автоконфигурация — это механизм, который анализирует ваш проект и автоматически настраивает приложение на основе добавленных вами библиотек (JAR-файлов).

    Как это работает? Представьте, что Spring Boot — это умный дворецкий. Когда вы запускаете приложение, он осматривает «комнату» (ваш classpath — список подключенных библиотек).

    * Если он видит на полке библиотеку для работы с базой данных (например, H2 или PostgreSQL), он автоматически создает подключение к базе данных. * Если он видит библиотеку spring-webmvc, он автоматически настраивает диспетчер сервлетов для обработки HTTP-запросов.

    Вам не нужно писать XML-файлы или Java-классы с настройками для стандартных ситуаций. Spring Boot делает это за вас, следуя принципу Convention over Configuration (соглашение важнее конфигурации).

    > «Spring Boot позволяет вам переопределить настройки, если это необходимо, но по умолчанию он предлагает разумные стандарты, которые подходят для 80% случаев».

    3. Встроенный веб-сервер (Embedded Server)

    Раньше процесс развертывания Java-приложения выглядел так:

  • Вы пишете код.
  • Упаковываете его в специальный архив (WAR-файл).
  • Скачиваете и устанавливаете на сервер отдельную программу — веб-сервер (например, Apache Tomcat или Jetty).
  • Загружаете ваш WAR-файл внутрь этого сервера.
  • Это было неудобно и сложно автоматизировать. Spring Boot предложил другой подход: Make JAR, not WAR.

    Веб-сервер (обычно Tomcat) уже находится внутри вашего приложения. Когда вы собираете проект, вы получаете один исполняемый JAR-файл, в котором есть и ваш код, и все библиотеки, и сам сервер. Чтобы запустить сайт, вам достаточно просто выполнить команду запуска Java-файла.

    Создание первого проекта: Spring Initializr

    Теперь перейдем от теории к практике. Самый простой способ создать проект на Spring Boot — использовать официальный генератор Spring Initializr.

    Шаг 1: Генерация проекта

  • Перейдите на сайт start.spring.io.
  • Вы увидите форму с настройками проекта. Давайте заполним её следующими значениями:
  • * Project: Maven (или Gradle, если вы предпочитаете его, но в курсе мы будем использовать Maven). * Language: Java. * Spring Boot: Выберите последнюю стабильную версию (без пометок SNAPSHOT или M1). * Project Metadata: * Group: com.example (или ваше доменное имя). * Artifact: demo. * Name: demo. * Package name: com.example.demo. * Packaging: Jar. * Java: 17 или 21 (в зависимости от того, какая версия установлена у вас).
  • В секции Dependencies (справа) нажмите кнопку «ADD DEPENDENCIES» и выберите:
  • * Spring Web (эта зависимость позволит нам создавать REST-контроллеры).
  • Нажмите кнопку GENERATE внизу страницы. Браузер скачает ZIP-архив.
  • !Интерфейс Spring Initializr для генерации базовой структуры проекта.

    Шаг 2: Структура проекта

    Распакуйте архив и откройте папку в вашей любимой IDE (IntelliJ IDEA, Eclipse или VS Code). Давайте посмотрим, что внутри:

    * pom.xml — файл управления сборкой Maven. Здесь прописаны наши зависимости, включая spring-boot-starter-web и spring-boot-starter-test. * src/main/java/com/example/demo/DemoApplication.java — точка входа в приложение. * src/main/resources/application.properties — файл для настроек (пока пустой).

    Шаг 3: Главный класс приложения

    Откройте файл DemoApplication.java. Вы увидите примерно такой код:

    Здесь ключевую роль играет аннотация @SpringBootApplication. Это мета-аннотация, которая объединяет в себе три другие:

  • @Configuration: обозначает, что класс может содержать настройки бинов.
  • @EnableAutoConfiguration: включает механизм автоконфигурации Spring Boot.
  • @ComponentScan: говорит Spring искать другие компоненты, конфигурации и сервисы в текущем пакете и его подпакетах.
  • Метод SpringApplication.run(...) запускает всё приложение, поднимает встроенный сервер Tomcat и инициализирует контекст Spring.

    Пишем код: Hello World

    Давайте заставим наше приложение отвечать на веб-запросы. Для этого нам нужно создать Контроллер.

    Создайте новый Java-класс рядом с DemoApplication.java (в том же пакете com.example.demo) и назовите его HelloController.java.

    Напишите следующий код:

    Разберем, что мы написали:

    * @RestController: Эта аннотация говорит Spring, что данный класс является веб-контроллером и его методы будут возвращать данные (а не HTML-страницы) напрямую в тело ответа. * @GetMapping("/hello"): Эта аннотация связывает HTTP-запрос типа GET по адресу /hello с методом sayHello().

    Запуск и проверка

  • Запустите метод main в классе DemoApplication (нажмите зеленый треугольник в вашей IDE).
  • В консоли вы увидите логи запуска. Дождитесь строки, похожей на: Tomcat started on port(s): 8080 (http).
  • Откройте браузер и перейдите по адресу: http://localhost:8080/hello.
  • Если вы всё сделали правильно, вы увидите на экране надпись: Привет, Spring Boot!

    Заключение

    Поздравляю! Вы только что создали свое первое приложение на Spring Boot. Мы разобрали, как Spring Boot упрощает жизнь разработчика за счет стартеров, автоконфигурации и встроенного сервера. Мы избавились от сложной XML-конфигурации и запустили веб-сервер прямо из Java-кода.

    В следующих статьях мы углубимся в работу с данными, научимся настраивать приложение через файлы свойств и разберем архитектуру REST API более детально. Spring Boot — это огромная экосистема, и мы сделали только первый шаг.

    2. Ядро фреймворка: понимание Inversion of Control, Dependency Injection и жизненного цикла бинов

    Ядро фреймворка: понимание Inversion of Control, Dependency Injection и жизненного цикла бинов

    В предыдущей статье мы создали наше первое приложение на Spring Boot, запустили его и увидели заветное «Привет, Spring Boot!» в браузере. Мы использовали аннотации вроде @RestController и @GetMapping, и всё заработало словно по волшебству. Но как именно Spring узнал, что нужно создать экземпляр нашего контроллера? Кто вызвал метод main? И как компоненты приложения находят друг друга?

    Ответы на эти вопросы кроются в самом сердце Spring Framework — в концепциях Inversion of Control (IoC) и Dependency Injection (DI). Понимание этих принципов отличает простого «кодера», копирующего примеры, от профессионального разработчика, способного проектировать сложные и масштабируемые системы.

    Проблема сильной связности (Tight Coupling)

    Чтобы понять решение, нужно сначала осознать проблему. Представьте, что мы пишем сервис для отправки уведомлений пользователям. В классическом Java-приложении (без фреймворков) это могло бы выглядеть так:

    В чем здесь проблема? Класс NotificationManager жестко связан с классом EmailService. Он сам создает его экземпляр через оператор new.

    Минусы такого подхода:

  • Сложность тестирования. Вы не сможете протестировать NotificationManager изолированно. При запуске теста всегда будет создаваться реальный EmailService, который может пытаться отправить реальные письма.
  • Сложность замены реализации. Если завтра бизнес решит отправлять SMS вместо Email, вам придется переписывать код NotificationManager.
  • Дублирование кода. Если EmailService нужен в 10 разных местах, вы будете создавать его 10 раз.
  • Inversion of Control (IoC): Принцип Голливуда

    Inversion of Control (Инверсия управления) — это архитектурный принцип, который меняет поток управления в программе. Вместо того чтобы ваш код управлял созданием объектов и их зависимостей, это управление передается внешнему контейнеру (фреймворку).

    Этот принцип часто называют «Принципом Голливуда»: «Не звоните нам, мы сами вам позвоним».

    В контексте Spring это означает:

    * Вы не создаете объекты с помощью new. * Вы описываете, какие объекты вам нужны. * Spring сам создает их и «подкладывает» в нужное место.

    !Визуализация отличия прямого управления зависимостями от инверсии управления.

    Dependency Injection (DI): Реализация магии

    Если IoC — это принцип (философия), то Dependency Injection (Внедрение зависимостей) — это конкретный паттерн проектирования, реализующий этот принцип. Это процесс, при котором объекты получают свои зависимости извне, вместо того чтобы создавать их самостоятельно.

    Давайте перепишем наш пример с использованием DI:

    Теперь NotificationManager не знает, откуда берется EmailService. Ему всё равно. Кто-то другой (Spring) должен создать EmailService и передать его в конструктор NotificationManager.

    Способы внедрения зависимостей в Spring

    Существует три основных способа внедрения зависимостей:

  • Constructor Injection (Внедрение через конструктор). Это рекомендуемый способ. Он гарантирует, что объект не может быть создан без необходимых зависимостей, и позволяет использовать final поля.
  • Setter Injection (Внедрение через сеттер). Используется редко, в основном для необязательных зависимостей.
  • Field Injection (Внедрение через поле). Использование аннотации @Autowired прямо над полем. Это самый короткий, но не рекомендуемый способ, так как он усложняет тестирование и скрывает зависимости класса.
  • Пример Field Injection (как делать не стоит, но вы часто это увидите):

    Spring IoC Container и Бины

    Где живут все эти объекты? Кто ими управляет? Этим занимается Spring IoC Container (Контейнер Spring).

    Что такое Бин (Bean)?

    Бин (Bean) — это просто объект, который управляется контейнером Spring. Это не какой-то специальный класс, это обычный Java-объект, но его жизненный цикл (создание, настройка, уничтожение) контролируется фреймворком.

    Чтобы превратить обычный класс в бин, мы используем аннотации. Самые популярные из них (стереотипы):

    * @Component: Базовая аннотация для любого компонента. * @Service: Для классов бизнес-логики. * @Repository: Для классов работы с базой данных (DAO). * @Controller / @RestController: Для обработки HTTP-запросов.

    Когда Spring Boot запускается, он сканирует ваш проект (начиная с пакета, где лежит класс с @SpringBootApplication), находит все классы с этими аннотациями, создает их экземпляры и помещает их в свой контейнер (ApplicationContext).

    !Схематичное изображение Spring IoC контейнера, управляющего бинами.

    Жизненный цикл бина (Bean Lifecycle)

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

    Основные этапы жизни бина:

  • Instantiate (Создание экземпляра). Spring вызывает конструктор класса.
  • Populate Properties (Внедрение зависимостей). Spring находит все необходимые зависимости (@Autowired или аргументы конструктора) и внедряет их.
  • Initialization (Инициализация). Если у бина есть методы инициализации, они выполняются. Здесь часто используется аннотация @PostConstruct.
  • Use (Использование). Бин готов к работе, приложение обрабатывает запросы.
  • Destruction (Уничтожение). Перед остановкой приложения Spring вызывает методы очистки. Используется аннотация @PreDestroy.
  • Пример использования хуков жизненного цикла:

    Области видимости бинов (Bean Scopes)

    По умолчанию Spring создает один экземпляр каждого бина на всё приложение. Это называется Singleton. Но иногда нам нужно другое поведение. За это отвечают Scope (области видимости).

    Основные Scope:

  • Singleton (по умолчанию). Один экземпляр на весь контейнер. Если 100 пользователей обратятся к сервису, они будут использовать один и тот же объект. Это эффективно по памяти. Важно: синглтон-бины не должны хранить состояние пользователя (state), иначе один пользователь увидит данные другого.
  • Prototype. Новый экземпляр создается каждый раз, когда бин запрашивается из контейнера.
  • Request (только для веб). Один экземпляр на каждый HTTP-запрос.
  • Session (только для веб). Один экземпляр на каждую сессию пользователя (например, корзина покупок).
  • Изменить Scope можно аннотацией @Scope:

    Почему это важно?

    Понимание IoC и DI — это фундамент. Без этого Spring кажется «черной магией», которая иногда ломается. Когда вы видите ошибку NoSuchBeanDefinitionException, теперь вы знаете: контейнер не смог найти инструкцию по созданию нужного объекта. А если видите CircularDependencyException, значит, Бин А требует Бин Б, а Бин Б требует Бин А, и конструктор зашел в тупик.

    В следующей статье мы разберем, как настраивать поведение этих бинов, не переписывая код, с помощью файлов конфигурации application.properties и профилей.

    3. Построение веб-слоя: разработка REST API, работа с контроллерами и валидация данных

    Построение веб-слоя: разработка REST API, работа с контроллерами и валидация данных

    В предыдущих статьях мы заложили фундамент нашего приложения. Мы узнали, как Spring Boot управляет зависимостями через стартеры и как работает магия Inversion of Control, создавая и связывая бины. Но пока наше приложение похоже на мощный двигатель, стоящий на стенде: он работает, крутится, но никуда не едет, потому что к нему не подключены колеса и руль.

    В этой статье мы создадим интерфейс для взаимодействия с внешним миром. Мы научимся строить REST API — тот самый слой, через который фронтенд (сайт), мобильные приложения или другие сервисы будут общаться с нашей программой.

    Что такое REST API?

    Прежде чем писать код, давайте разберемся с теорией. REST (Representational State Transfer) — это архитектурный стиль взаимодействия компонентов распределенного приложения в сети. Проще говоря, это набор правил того, как программы должны «разговаривать» друг с другом через интернет.

    В основе REST лежит понятие ресурса. Ресурс — это любая сущность, которой оперирует ваше приложение: Пользователь, Товар, Заказ, Статья. У каждого ресурса есть уникальный адрес (URI).

    Для действий над ресурсами используются стандартные HTTP-методы (глаголы):

    * GET: Получить данные (например, список товаров или конкретный товар). * POST: Создать новый ресурс (добавить товар). * PUT: Обновить существующий ресурс целиком. * DELETE: Удалить ресурс.

    !Визуализация базового принципа запрос-ответ в REST архитектуре.

    Контроллеры: Точка входа

    В Spring Boot за обработку входящих HTTP-запросов отвечают Контроллеры. Это бины, помеченные специальными аннотациями.

    @RestController vs @Controller

    В Spring MVC (веб-части фреймворка) есть две основные аннотации:

  • @Controller: Классический контроллер, который обычно используется для возврата HTML-страниц (с помощью шаблонизаторов вроде Thymeleaf).
  • @RestController: Специализированная версия, которая объединяет @Controller и @ResponseBody. Она говорит Spring: «Результат выполнения методов этого класса нужно записать прямо в тело HTTP-ответа в формате JSON».
  • Поскольку мы строим REST API, мы будем использовать @RestController.

    Создание первого эндпоинта

    Представим, что мы делаем систему управления библиотекой. Создадим контроллер для работы с книгами.

    Разберем аннотации:

    * @RequestMapping("/api/books"): Задает базовый адрес для всех методов в этом классе. Теперь все запросы к этому контроллеру должны начинаться с /api/books. * @GetMapping: Говорит, что метод getAllBooks обрабатывает HTTP-запрос типа GET. Поскольку мы не указали дополнительный путь в скобках, он сработает на базовый адрес /api/books.

    Если вы запустите приложение и перейдете по адресу http://localhost:8080/api/books, вы увидите JSON-массив с названиями книг.

    Передача параметров: PathVariable и RequestParam

    Часто нам нужно получить не все данные, а что-то конкретное. Для передачи данных от клиента к серверу в GET-запросах есть два основных способа.

    1. @PathVariable (Переменная пути)

    Используется, когда параметр является частью адреса ресурса. Обычно это ID сущности.

    Пример запроса: GET /api/books/1 (получить книгу с ID 1).

    Здесь {index} в аннотации совпадает с именем аргумента метода. Spring автоматически извлечет число из URL и передаст его в метод.

    2. @RequestParam (Параметр запроса)

    Используется для фильтрации, сортировки или поиска. Эти параметры передаются после знака вопроса ?.

    Пример запроса: GET /api/books/search?query=Мир.

    !Наглядное различие между передачей параметров через путь и через строку запроса.

    Работа с данными: DTO и RequestBody

    Когда мы хотим создать новую книгу (метод POST), нам нужно передать данные о ней на сервер. Передавать сложные объекты через URL неудобно и небезопасно. Для этого используется тело запроса (Request Body).

    Паттерн DTO (Data Transfer Object)

    Хорошей практикой считается не использовать сущности базы данных (Entity) напрямую в контроллерах. Вместо этого создаются специальные классы — DTO. Это простые POJO (Plain Old Java Objects), которые служат только для переноса данных.

    Почему это важно?

  • Безопасность: Вы не хотите, чтобы пользователь мог случайно изменить поле isAdmin или passwordHash, просто передав их в JSON.
  • Разделение ответственности: Структура базы данных может меняться, но внешний API должен оставаться стабильным.
  • Создадим класс BookDto:

    Теперь создадим метод для добавления книги:

    Аннотация @RequestBody говорит Spring: «Возьми JSON из тела запроса и преврати его в объект BookDto».

    Валидация данных

    Золотое правило веб-разработки: никогда не доверяй данным, пришедшим от клиента. Пользователь может прислать пустую строку вместо названия, отрицательную цену или email без знака @.

    Spring Boot предоставляет мощный механизм валидации, основанный на спецификации Bean Validation (Hibernate Validator).

    Шаг 1: Зависимость

    Если вы используете свежие версии Spring Boot, валидация может не входить в starter-web. Убедитесь, что в pom.xml есть:

    Шаг 2: Аннотации ограничений

    Добавим правила проверки прямо в наш DTO:

    Самые популярные аннотации:

    * @NotNull: Значение не должно быть null. * @NotBlank: Строка не должна быть пустой или состоять только из пробелов. * @Min / @Max: Для чисел. * @Email: Проверка формата email. * @Pattern: Проверка по регулярному выражению.

    Шаг 3: Включение валидации в контроллере

    Просто добавить аннотации в класс недостаточно. Нужно сказать контроллеру, чтобы он проверил объект. Для этого используется аннотация @Valid перед аргументом метода.

    Если валидация не пройдет, Spring автоматически выбросит исключение MethodArgumentNotValidException и вернет клиенту ошибку 400 (Bad Request).

    Управление ответами: ResponseEntity

    До сих пор наши методы возвращали просто строки или списки. При этом Spring по умолчанию ставит статус ответа 200 OK. Но в REST API важно использовать правильные статусы.

    * Если ресурс создан — 201 Created. * Если ресурс не найден — 404 Not Found. * Если ошибка сервера — 500 Internal Server Error.

    Для полного контроля над ответом используется класс ResponseEntity.

    Или пример с обработкой отсутствия книги:

    Заключение

    Сегодня мы превратили наше Java-приложение в полноценный веб-сервис. Мы разобрали:

  • Как принимать запросы с помощью @RestController.
  • Как извлекать данные из пути (@PathVariable) и параметров запроса (@RequestParam).
  • Как безопасно принимать сложные объекты через DTO и @RequestBody.
  • Как защитить приложение от некорректных данных с помощью валидации.
  • Как управлять HTTP-статусами через ResponseEntity.
  • Теперь наш «двигатель» имеет управление. Но есть одна проблема: все данные (наш список книг) хранятся в оперативной памяти. Стоит перезапустить приложение — и все книги исчезнут. В следующей статье мы решим эту проблему, подключив настоящую базу данных и научившись работать с ней через Spring Data JPA.

    4. Работа с данными: подключение баз данных, ORM Hibernate и магия Spring Data JPA

    Работа с данными: подключение баз данных, ORM Hibernate и магия Spring Data JPA

    В предыдущей статье мы научились строить REST API, принимать запросы и отправлять ответы. Мы создали контроллер для нашей библиотеки, который умеет добавлять и показывать книги. Однако у нашего приложения был критический недостаток: все данные хранились в обычном списке List в оперативной памяти. Стоило нам перезапустить приложение, как все добавленные книги исчезали.

    В реальных корпоративных системах данные должны жить дольше, чем процесс Java. Они должны сохраняться на жестком диске в надежном хранилище — базе данных. Сегодня мы узнаем, как подключить базу данных к Spring Boot, что такое ORM и почему разработчики Spring почти никогда не пишут SQL-запросы вручную.

    Эволюция доступа к данным

    Чтобы оценить красоту Spring Data, нужно понять, как разработчики страдали раньше.

    Уровень 1: JDBC (Java Database Connectivity)

    Это самый низкоуровневый способ работы с БД в Java. Чтобы получить данные, вам нужно:

  • Открыть соединение.
  • Написать SQL-запрос в виде строки: "SELECT * FROM books WHERE id = ?".
  • Подставить параметры.
  • Выполнить запрос.
  • Пробежаться по полученным строкам (ResultSet) и вручную переложить данные из колонок таблицы в поля Java-объекта.
  • Закрыть соединение (и не забыть обработать исключения).
  • Это порождало огромное количество шаблонного кода (boilerplate code).

    Уровень 2: ORM (Object-Relational Mapping)

    Программисты заметили, что они постоянно делают одно и то же: превращают строки таблицы в объекты и наоборот. Так родилась концепция ORM — объектно-реляционного отображения.

    Идея проста: давайте договоримся, что Класс в Java соответствует Таблице в БД, а Поле класса — Колонке.

    !Иерархия технологий доступа к данным в экосистеме Spring.

    JPA и Hibernate: В чем разница?

    На собеседованиях часто спрашивают: «Чем отличается JPA от Hibernate?». Это фундаментальный вопрос.

    * JPA (Jakarta Persistence API) — это спецификация (стандарт). Это просто набор интерфейсов и правил, описывающих, как Java-программы должны работать с базами данных. Это как правила дорожного движения. * Hibernate — это реализация этой спецификации. Это конкретная библиотека, которая выполняет всю работу. Это как автомобиль, который едет по правилам.

    Spring Boot использует Hibernate по умолчанию, но скрывает его за еще одним слоем абстракции — Spring Data JPA.

    Подключение базы данных

    Давайте модифицируем наше приложение «Библиотека», чтобы оно сохраняло книги по-настоящему.

    Шаг 1: Зависимости

    Нам понадобятся две библиотеки. Добавьте их в pom.xml (или выберите при создании проекта в Spring Initializr):

  • spring-boot-starter-data-jpa — подключает Spring Data, JPA и Hibernate.
  • com.h2database:h2 — драйвер базы данных H2.
  • > Почему H2? H2 — это база данных, написанная на Java, которая может работать прямо в оперативной памяти или сохранять данные в файл без установки отдельного сервера. Для обучения это идеальный вариант. В реальном проекте вы замените её на PostgreSQL или MySQL, просто поменяв одну строчку в настройках.

    Шаг 2: Настройка

    Откройте файл src/main/resources/application.properties. Добавим настройки:

    Создание Сущности (Entity)

    Теперь превратим наш простой класс Book (или BookDto, но лучше создать отдельный класс для БД) в полноценную сущность.

    Когда вы запустите приложение, Hibernate увидит аннотацию @Entity, проверит базу данных и, если таблицы books нет, создаст её с колонками id, title и author.

    Магия Spring Data: Репозитории

    В классическом Hibernate нам пришлось бы писать класс DAO (Data Access Object), открывать сессии и управлять транзакциями. Spring Data JPA предлагает революционный подход: вы объявляете интерфейс, а Spring генерирует реализацию на лету.

    Создайте интерфейс BookRepository:

    Это всё! Мы ничего не написали внутри, но, унаследовавшись от JpaRepository, мы автоматически получили методы:

    * save(Book book) — сохранить или обновить. * findById(Long id) — найти по ID. * findAll() — найти все. * deleteById(Long id) — удалить. * count() — количество записей.

    Здесь Book — это тип сущности, а Long — тип её ID.

    Использование в Контроллере

    Теперь внедрим репозиторий в наш контроллер вместо старого списка.

    Теперь, если вы отправите POST-запрос, данные запишутся в H2. Если вы перезагрузите приложение, данные исчезнут (потому что H2 по умолчанию в памяти), но если бы мы подключили PostgreSQL, они бы сохранились навсегда.

    Derived Query Methods (Магия имен методов)

    Представьте, что нам нужно найти все книги конкретного автора. Стандартного метода findAllByAuthor в JpaRepository нет. Нужно ли писать SQL? Нет.

    Spring Data умеет парсить имена методов и превращать их в запросы. Добавьте этот метод в интерфейс BookRepository:

    Spring увидит префикс findBy, увидит поле Author и автоматически сгенерирует SQL-запрос вида: SELECT * FROM books WHERE author = ?

    Вы можете создавать очень сложные запросы просто именуя методы:

    * findByTitleContaining(String text) — поиск по части названия (LIKE). * findByPriceGreaterThan(Integer price) — поиск книг дороже определенной цены. * findByAuthorAndTitle(String author, String title) — поиск по двум полям (AND).

    JPQL и @Query

    Если запрос слишком сложный для генерации по имени, вы можете написать его на языке JPQL (Java Persistence Query Language). Это похоже на SQL, но вместо имен таблиц вы используете имена классов.

    Заключение

    Сегодня мы сделали огромный шаг вперед. Мы отказались от ручного управления данными и доверили это профессионалам — Hibernate и Spring Data JPA.

    Мы узнали:

  • Entity — это зеркало таблицы в Java-коде.
  • Repository — это интерфейс, который дает нам готовые методы для работы с БД.
  • H2 — удобная база для разработки.
  • Query Methods — способ создавать запросы, просто правильно называя методы.
  • Теперь наше приложение имеет полноценный бэкенд с базой данных. Но что, если бизнес-логика станет сложнее? Что если при создании книги нужно отправлять email автору или проверять сложные условия? Писать всё это в Контроллере — плохая практика. В следующей статье мы разберем Сервисный слой (Service Layer) и научимся правильно структурировать бизнес-логику.

    5. Безопасность и продакшн: основы Spring Security, конфигурация и мониторинг через Actuator

    Безопасность и продакшн: основы Spring Security, конфигурация и мониторинг через Actuator

    Поздравляю! Мы прошли долгий путь. Мы начали с создания простого веб-сервиса, научились обрабатывать REST-запросы, подключили базу данных и настроили автоматическое сохранение сущностей. Наше приложение «Библиотека» работает: можно добавлять книги, искать их и просматривать список.

    Но есть одна проблема. Сейчас наше приложение похоже на библиотеку с открытыми дверями, где нет ни библиотекаря, ни охраны. Любой прохожий может зайти, забрать все книги или даже сжечь их (отправить DELETE-запрос). В реальном мире («продакшене») так делать нельзя.

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

    Часть 1: Spring Security — Щит вашего приложения

    Spring Security — это стандарт де-факто для защиты Java-приложений. Это мощный, гибкий и, честно говоря, иногда пугающий своей сложностью фреймворк. Но Spring Boot делает его настройку максимально простой.

    Аутентификация и Авторизация

    Прежде чем писать код, нужно четко понимать разницу между двумя главными понятиями безопасности:

  • Аутентификация (Authentication) — ответ на вопрос «Кто ты?». Это процесс проверки личности пользователя (обычно через логин и пароль).
  • Авторизация (Authorization) — ответ на вопрос «Что тебе можно делать?». Это проверка прав доступа уже аутентифицированного пользователя к конкретному ресурсу.
  • !Визуальная разница между проверкой личности и проверкой прав доступа.

    Подключение Spring Security

    Добавим зависимость в наш pom.xml:

    Как только вы добавите эту зависимость и перезапустите приложение, произойдет магия Spring Boot:

  • Все эндпоинты вашего приложения станут защищенными.
  • При попытке зайти на http://localhost:8080/api/books вы увидите стандартную форму входа.
  • В консоли при запуске появится сгенерированный пароль (UUID).
  • Логин по умолчанию — user.

    Настройка прав доступа

    Конечно, стандартный пароль в консоли нам не подходит. Нам нужно настроить пользователей и правила. В современных версиях Spring Security (начиная с версии 5.7 и Spring Boot 3.0) мы больше не наследуемся от WebSecurityConfigurerAdapter. Вместо этого мы объявляем бин SecurityFilterChain.

    Создадим класс конфигурации SecurityConfig.java:

    Разберем ключевые моменты:

    * requestMatchers("/api/books").permitAll(): Мы разрешили метод GET для всех. Библиотека открыта для чтения. * .hasRole("ADMIN"): Методы POST, PUT, DELETE по этому пути будут доступны только пользователю с ролью ADMIN. * InMemoryUserDetailsManager: Мы храним пользователей в оперативной памяти. В реальном проекте здесь будет подключение к базе данных через JdbcUserDetailsManager или кастомный сервис, но принцип останется тем же — Spring Security запрашивает пользователя по имени, а сервис возвращает объект UserDetails.

    Теперь, если вы попытаетесь удалить книгу через Postman без пароля, вы получите ошибку 401 Unauthorized. Если зайдете под обычным пользователем — 403 Forbidden. И только админ сможет управлять фондом библиотеки.

    Часть 2: Мониторинг с Spring Boot Actuator

    Представьте, что ваше приложение работает на сервере уже месяц. Как узнать, всё ли с ним в порядке? Хватает ли ему памяти? Доступна ли база данных? Не упал ли он час назад?

    Для ответов на эти вопросы существует Spring Boot Actuator. Это набор готовых эндпоинтов для мониторинга и управления приложением.

    Подключение

    Добавьте зависимость:

    Основные эндпоинты

    После запуска Actuator добавляет префикс /actuator к своим путям. Самые полезные из них:

  • /actuator/health: Показывает состояние здоровья приложения. По умолчанию возвращает просто {"status": "UP"}. Если база данных отвалится, статус сменится на DOWN.
  • /actuator/info: Показывает информацию о версии приложения (если настроено).
  • /actuator/metrics: Огромный список метрик (использование CPU, памяти, количество HTTP-запросов).
  • Чтобы увидеть подробности здоровья (например, есть ли место на диске и пинг до БД), нужно добавить настройку в application.properties:

    Теперь запрос на /actuator/health вернет детальный JSON:

    > Важно: Эндпоинты Actuator могут раскрывать чувствительную информацию. В продакшене обязательно закрывайте их с помощью Spring Security, разрешая доступ только для админов или специальных сервисов мониторинга (например, Prometheus).

    Часть 3: Конфигурация и Профили

    Разрабатывая приложение, мы использовали базу данных H2, которая живет в памяти. Но на продакшене нам нужна надежная PostgreSQL или MySQL. Как сделать так, чтобы на компьютере разработчика использовалась одна база, а на сервере — другая, не переписывая код?

    На помощь приходят Профили (Profiles).

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

    Spring Boot позволяет создавать отдельные файлы настроек для разных окружений. Формат имени файла: application-{profile}.properties.

    Создадим два файла рядом с основным application.properties:

    1. application-dev.properties (для разработки):

    2. application-prod.properties (для боевого сервера):

    Переключение профилей

    Чтобы активировать нужный профиль, достаточно указать его в основном файле application.properties:

    Или передать как аргумент при запуске JAR-файла:

    Теперь ваше приложение стало гибким. Оно знает, как вести себя в песочнице разработчика и как работать под нагрузкой на реальном сервере.

    Заключение курса

    Поздравляю! Вы завершили курс «Основы разработки на Spring Boot». Давайте оглянемся назад и посмотрим, что мы построили.

    Мы начали с чистого листа и концепции Inversion of Control. Мы поняли, как Spring управляет бинами и зависимостями. Затем мы создали REST API, научившись общаться с внешним миром. Мы подключили базу данных через Spring Data JPA, забыв о написании SQL вручную. И, наконец, сегодня мы защитили наше приложение с Spring Security и подготовили его к жизни на сервере с Actuator.

    Spring Boot — это огромная вселенная. Мы изучили только основы, но этот фундамент позволит вам двигаться дальше: изучать микросервисы, реактивное программирование, облачные технологии и многое другое. Главное — вы больше не боитесь магии Spring, потому что знаете, как она работает изнутри.

    Удачи в разработке!