|
|||||||
Как писать на Spring в 2017
Время создания: 07.06.2019 19:16
Раздел: INFO - Development - Spring
Запись: wwwlir/Tetra/master/base/1559924190hskf1s7xzr/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
В одной из классических статей для новичков, мелькавших недавно на Хабре, рассказывалось про создание базового 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 MVC, Data, Security. Ниже мы увидим, как это все работает вместе. Особняком стоит отметить Spring Boot — это вишенка на торте (а некоторые думают, что собственно сам торт), которые позволяет избежать всего ужаса XML конфигурации. Boot позволяет быстро создать и сконфигурить (т.е. настроить зависимости между компонентами) приложение, упаковать его в исполняемый самодостаточный артефакт. Это то связующее звено, которое объединяет вместе набор компонентов в готовое приложение. Пару вещей, которые нужно знать про Spring Boot:
Для того, чтобы создать простое приложение, знать, как создать проект 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 и создаем проект с зависимостями 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);
}
}
Пара вещей, на которые стоит обратить внимание.
С Котлин это бы выглядело еще лучше и проще, но это потребует введения сразу большого количества новых понятий — язык, фреймворк. Лучше начинать с малого. Класс, помеченный как @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 "под капотом" происходит следующее:
Чтобы использовать репозиторий в контроллере мы воспользуемся механизмом внедрения зависимостей, предоставляемый 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();
}
}
На что обратить внимание:
Теперь при запросе 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 это все таки отдельная тема, поэтому мы не будем сильно на этом останавливаться сейчас. Чтобы собрать и запустить наше приложение в продакшене есть несколько вариантов.
Таким образом сборка и запуск приложения выглядит как:
Для деплоймента этого JAR файла не нужно ничего, кроме установленной Java (JRE). Это так называемый fat JAR — он включает в себя и встроенный сервлет контейнер (Tomcat по умолчанию) и фреймворк, и все библиотеки-зависимости. По сути, он является единственным артефактом деплоймтента — его можно просто копировать на целевой сервер и запускать там. Более того, файл можно сделать "выполняемым" и запускать его просто из командной строки (Java, конечно, все равно необходима). На базе этого файла можно легко создать Docker образ или установить его как демон. Больше деталей доступно в официальной документации. Получилось, все же, очень сжато — но уложить даже самый простой вводный курс по Spring в рамки одной статьи не очень просто. Надеюсь, это поможет кому-то сделать первый шаги в Spring-е, и хотя понять его фундаментальные концепции. Как вы успели заметить, в тексте статьи много раз звучало слово "магия Spring". По сути своей, это очень "магический" фреймворк — даже взглянув на самую верхушку айсберга мы уже видели, что Spring много всего делает в фоне. Это является и плюсом, и минусом фреймворка. Плюс несомненно в том, что многие сложные вещи (очень многие) можно сделать одной аннотацией или зависимостью. Минус же это скрытая сложность — чтобы решить какие-то сложные проблемы, заставить фреймворк работать в крайних случаях или понимать все тонкости и аспекты нужно его неплохо знать. Чтобы сделать этап "знать" как можно проще, Spring обладает отличной документацией, огромным сообществом, и чистыми исходниками, которые вполне можно читать. Если расположить Spring на шкале Рича Хики, он (Spring) несомненно попадет в easy, но уж точно не simple. Но для современного энтерпрайза (и не только энтерпрайза) он дает невероятные возможности чтобы получить production-ready приложение очень быстро и концентрироваться на логике приложения, а не инфраструктуры вокруг.
Теги: |
|||||||
|
|||||||
|