- PVSM.RU - https://www.pvsm.ru -
В одной из классических статей для новичков, мелькавших недавно на Хабре, рассказывалось про создание базового Web приложения на Java. Все начиналось с сервлета, потом создания JSP страницы и, наконец, деплоймента в контейнер. Посмотрев на это свежим взглядом я понял, что для как раз для новичков это, наверняка, выглядит совершенно жутко — на фоне простых и понятных PHP или Node.js, где все просто — написал контроллер, вернул объект, он стал JSON или HTML. Чтобы немного развеять это ощущение, я решил написать "Гайд для новичков в Spring". Цель это статьи — показать, что создание Web приложений на Java, более того — на Spring Framework это не боль и мучительное продирание через web.xml, persistence.xml, beans.xml, и собирание приложения как карточного домика по кусочкам, а вполне себе быстрый и комфортный процесс. Аудитория — начинающие разработчики, разработчики на других языках, ну и те, кто видел Спринг в его не самые лучше времена.
В этой статье мы посмотрим, что включает в себя современный Спринг, как настроить локальное окружение для разработки Веб приложений, и создадим простое веб-приложение, которое берет данные из БД и отдает HTML страницу и JSON. Как ни странно, большинство статей (на русском языке) для начинающих, которые я нашел в топе поиска описывают и ручное создание контекста, и запуск приложения, и конфигурацию через XML — ничего из этого в современном Спринге делать, разумеется, не обязательно.
Для начала пара слов, что же такое Spring. В настоящее время, под термином "Spring" часто подразумевают целое семейство проектов. В большинстве своем, они развиваются и курируются компанией Pivotal и силами сообщества. Ключевые (но не все) проекты семейства Spring это:
Spring Framework (или Spring Core)
Ядро платформы, предоставляет базовые средства для создания приложений — управление компонентами (бинами, beans), внедрение зависимостей, MVC фреймворк, транзакции, базовый доступ к БД. В основном это низкоуровневые компоненты и абстракции. По сути, неявно используется всеми другими компонентами.
Spring MVC (часть Spring Framework)
Стоит упомянуть отдельно, т.к. мы будем вести речь в основном о веб-приложениях. Оперирует понятиями контроллеров, маппингов запросов, различными HTTP абстракциями и т.п. Со Spring MVC интегрированы нормальные шаблонные движки, типа Thymeleaf, Freemaker, Mustache, плюс есть сторонние интеграции с кучей других. Так что никакого ужаса типа JSP или JSF писать не нужно.
Spring Data
Доступ к данным: реляционные и нереляционные БД, KV хранилища и т.п.
Spring Cloud
Много полезного для микросервисной архитектуры — service discovery, трасировка и диагностика, балансировщики запросов, circuit breaker-ы, роутеры и т.п.
Spring Security
Авторизация и аутентификация, доступ к данным, методам и т.п. OAuth, LDAP, и куча разных провайдеров.
Типичное веб приложение скорее всего будет включать набор вроде Spring MVC, Data, Security. Ниже мы увидим, как это все работает вместе.
Особняком стоит отметить Spring Boot — это вишенка на торте (а некоторые думают [1], что собственно сам торт), которые позволяет избежать всего ужаса XML конфигурации. Boot позволяет быстро создать и сконфигурить (т.е. настроить зависимости между компонентами) приложение, упаковать его в исполняемый самодостаточный артефакт. Это то связующее звено, которое объединяет вместе набор компонентов в готовое приложение. Пару вещей, которые нужно знать про Spring Boot:
localhost:27017
localhost
Для того, чтобы создать простое приложение, знать, как создать проект Maven с нуля, как настроить плагины, чтобы создать JAR, какие бывают лейауты в JAR, как настроить Surefire для запуска тестов, как установить и запустить локально Tomcat, а уж тем более, как работает DispatcherServlet — совершенно не нужно.
Современное приложение на Spring создается в два шага:
Spring Initializr позволяет "набрать" в свое приложение нужных компонентов, которые потом Spring Boot (он автоматически включен во все проекты, созданные на Initializr) соберет воедино.
В качестве среды разработки подойдет что угодно, например бесплатная IntelliJ IDEA CE прекрасно справляется — просто импортируйте созданный pom.xml (Maven) или build.gradle (Gradle) файл в IDE.
Стоит отдельно отметить компонент Spring Boot который называется DevTools. Он решает проблему цикла локальной разработки, который раньше выглядел как:
В те древние времена даже родилась поговорка, что Spring это DSL для конвертации XML конфигов в стектрейсы.
С включенными Spring Boot DevTools цикл разработки сокращается до:
DevTools будут автоматом проверять изменения в скомпилированном коде или шаблонах, и очень быстро перезапускать (hot reload) только "боевую" часть приложения (как nodemon, если вы знакомы с миром node.js). Более того, DevTools включают интеграцию с Live Reload и после установки расширения в браузере, достаточно скомпилировать проект в IDEA, чтобы он автоматом обновился в браузере.
Окей, пора приступать к практической части. Итак, наша цель — создать веб-приложение, которое отдает welcome страницу, обращается с нее же к собственному API, получает JSON с данными из базы и выводит их в таблицу.
Для начала, идем на start.spring.io [2] и создаем проект с зависимостями Web, DevTools, JPA (доступ к реляционным базам), H2 (простая in-memory база), Mustache (движок шаблонов). Сгенерированный pom.xml
импортируем в IDEA. Все, приложение готово к запуску! Можно его запустить из командной строки командой ./mvnw spring-boot:run
или прямо из IDEA — запустив метод main. Да, серверов приложений, контейнеров и деплоймента не нужно.
Точнее, контейнер нужен — только он предоставлен и настроен Spring Boot-ом — используя Embedded Tomcat
Итак, наш следующий шаг — создать контроллер и вернуть "домашнюю" страницу. Код контроллера выглядит так просто, как и ожидается:
@Controller
public class IndexController {
@GetMapping("/")
public ModelAndView index() {
Map<String, String> model = new HashMap<>();
model.put("name", "Alexey");
return new ModelAndView("index", model);
}
}
Пара вещей, на которые стоит обратить внимание.
ModelAndView
— дальше Spring знает, что нужно взять вью index.html
из папки resources/templates
(это соглашение по умолчанию) и передать туда модельС Котлин это бы выглядело еще лучше и проще, но это потребует введения сразу большого количества новых понятий — язык, фреймворк. Лучше начинать с малого.
Класс, помеченный как @Controller
автоматически регистрируется в MVC роутере, а используя аннотации @(Get|Post|Put|Patch)Mapping
можно регистрировать разные пути.
Все файлы из каталога
resources/static/
считаются статическими, там можно хранить CSS и картинки.
Мы используем Mustache (Handlebar) синтаксис, поэтому шаблон очень напоминает обычный HTML
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Welcome to Spring, {{ name }}</h1>
</body>
</html>
После компиляции проекта (⌘/Ctrl + F9) — можно сразу идти на http://localhost:8080
и увидеть созданную страницу.
Для начала, опишем нашу предметную область. Мы будем собирать статистику посещений — каждый раз, когда кто-то заходит на главную страницу, мы будем писать это в базу. Модель выглядит до крайности примитивно:
@Entity
public class Visit {
@Id
@GeneratedValue
public Long id;
public String description;
}
Предвидя череду комментариев "Как же без геттеров и сеттеров" и "Где же equals / hashCode" — эти элементы упущены сознательно с целью упрощения кода. Совершенно чудовищная ошибка дизайна Java которая заставляет писать эту ерунду (геттеры и методы сравнения), это, конечно, отдельный разговор. Котлин эту проблему, кстати, решает.
Мы снова очень активно используем аннотации — в этот раз из Spring Data (точнее, JPA — это дремучая спецификация для доступа к данным). Этот класс описывает модель с двумя полями, одно из которых генерится автоматически. По этому классу будет автоматически создана модель данных (таблицы) в БД.
Теперь для этой модели пора создать репозиторий. Это еще проще, чем контроллер.
@Repository
public interface VisitsRepository extends CrudRepository<Visit, Long> {
}
Все, репозиторий можно использовать для работы с базой — читать и писать записи. У внимательного читателя должен сработать WTF детектор — что здесь вообще происходит? Мы определяем интерфейс и внезапно он начинает работать с базой? Все так. Благодаря магии Spring Boot и Spring Data "под капотом" происходит следующее:
DataSource
(это ключевой компонент для подключения к базе) чтобы приложение работало с этой базойCrudRepository
и автоматически генерит для них дефолтные реализации, которые включают базовые методы репозитория, типа findOne
, findAll
, save
etc. @Repository
этот компонент становится доступным в нашем приложении (и мы его используем через пару минут)Чтобы использовать репозиторий в контроллере мы воспользуемся механизмом внедрения зависимостей, предоставляемый Spring Framework. Чтобы это сделать, как ни странно, нужно всего лишь объявить зависимость в нашем контроллере.
@Controller
public class IndexController {
final VisitsRepository visitsRepository;
public IndexController(VisitsRepository visitsRepository) {
this.visitsRepository = visitsRepository;
}
...
}
Увидев в нашем конструкторе параметр типа VisitRepository
, Spring найдет созданный Spring Data-ой репозиторий и передаст его в конструктор.
Теперь можно писать в базу в методе контроллера.
@GetMapping("/")
public ModelAndView index() {
Map<String, String> model = new HashMap<>();
model.put("name", "Alexey");
Visit visit = new Visit();
visit.description = String.format("Visited at %s", LocalDateTime.now());
visitsRepository.save(visit);
return new ModelAndView("index", model);
}
Следующий шаг — это вернуть все записи из базы в JSON формате, чтобы потом их можно было читать на клиенте.
Для REST в Spring есть отдельный тип контроллера который называется @RestController
, код которого не сильно отличается от обычного контроллера.
@RestController
@RequestMapping("/api")
public class ApiController {
final VisitsRepository visitsRepository;
public ApiController(VisitsRepository visitsRepository) {
this.visitsRepository = visitsRepository;
}
@GetMapping("/visits")
public Iterable<Visit> getVisits() {
return visitsRepository.findAll();
}
}
На что обратить внимание:
@RequestMapping
Теперь при запросе http://localhost:8080/api/visits
(предварительно скомпилировав проект и дав DevTools обновить приложение) мы получим JSON с нужными данными.
Оставим за рамками этой статьи, пример можно увидеть в исходном коде. Цель этого кода — исключительно продемонстрировать как получить JSON данные с сервера, интеграции с клиентскими фреймворками React, Angular etc намеренно оставлены вне рамок этой статьи.
Spring так же предоставляет мощные средства для Integration и Unit тестирования приложения. Пример кода, который проверяет контроллер:
@Test
public void indexControllerShouldReturnHtmlPage() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Welcome to Spring")));
}
Используя абстракции типа MockMvc
можно легко тестировать внешний интерфейс приложения, в то же время имея доступ к его внутренностям. Например, можно целиком заменить компоненты приложения на моки (заглушки).
Аналогично для API тестов есть набор хелперов для проверки JsonPath выражений.
@Test
public void apiControllerShouldReturnVisits() throws Exception {
mockMvc.perform(get("/"));
mockMvc.perform(get("/api/visits"))
.andExpect(jsonPath("$.*.description", iterableWithSize(1)));
}
Тестирование в Spring это все таки отдельная тема, поэтому мы не будем сильно на этом останавливаться сейчас.
Чтобы собрать и запустить наше приложение в продакшене есть несколько вариантов.
Таким образом сборка и запуск приложения выглядит как:
./mvnw package
java -jar ./target/demo-0.0.1-SNAPSHOT.jar
Для деплоймента этого JAR файла не нужно ничего, кроме установленной Java (JRE). Это так называемый fat JAR — он включает в себя и встроенный сервлет контейнер (Tomcat по умолчанию) и фреймворк, и все библиотеки-зависимости. По сути, он является единственным артефактом деплоймтента — его можно просто копировать на целевой сервер и запускать там.
Более того, файл можно сделать "выполняемым" и запускать его просто из командной строки (Java, конечно, все равно необходима).
На базе этого файла можно легко создать Docker образ или установить его как демон. Больше деталей доступно в официальной документации [3].
Получилось, все же, очень сжато — но уложить даже самый простой вводный курс по Spring в рамки одной статьи не очень просто. Надеюсь, это поможет кому-то сделать первый шаги в Spring-е, и хотя понять его фундаментальные концепции.
Как вы успели заметить, в тексте статьи много раз звучало слово "магия Spring". По сути своей, это очень "магический" фреймворк — даже взглянув на самую верхушку айсберга мы уже видели, что Spring много всего делает в фоне. Это является и плюсом, и минусом фреймворка. Плюс несомненно в том, что многие сложные вещи (очень многие) можно сделать одной аннотацией или зависимостью. Минус же это скрытая сложность — чтобы решить какие-то сложные проблемы, заставить фреймворк работать в крайних случаях или понимать все тонкости и аспекты нужно его неплохо знать.
Чтобы сделать этап "знать" как можно проще, Spring обладает отличной документацией, огромным сообществом, и чистыми исходниками, которые вполне можно читать. Если расположить Spring на шкале Рича Хики, он (Spring) несомненно попадет в easy, но уж точно не simple. Но для современного энтерпрайза (и не только энтерпрайза) он дает невероятные возможности чтобы получить production-ready приложение очень быстро и концентрироваться на логике приложения, а не инфраструктуры вокруг.
Автор: alek_sys
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/260836
Ссылки в тексте:
[1] думают: https://image.slidesharecdn.com/qdtv5ey1qn2pzulfxfat-signature-0f65932498c4fd6ca0dcfb14db8e328e12718ad0dbf9ecc6960c390477574705-poli-160917122451/95/java-day-kharkiv-integration-testing-from-the-trenches-rebooted-64-638.jpg?cb=1474115226
[2] Spring Initializr: https://start.spring.io
[3] официальной документации: https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html
[4] github: https://github.com/alek-sys/spring-demo
[5] spring.io: https://spring.io/guides
[6] документация: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
[7] Источник: https://habrahabr.ru/post/333756/
Нажмите здесь для печати.