автордың кітабын онлайн тегін оқу Spring Boot 2: лучшие практики для профессионалов
Научный редактор В. Дмитрущенков
Переводчик И. Пальти
Технический редактор Е. Рафалюк-Бузовская
Литературный редактор Е. Рафалюк-Бузовская
Художники А. Барцевич, Н. Гринчик, В. Мостипан
Корректоры О. Андриевич, Н. Гринчик, В. Дмитрущенков, Е. Павлович
Верстка Г. Блинов
Фелипе Гутьеррес
Spring Boot 2: лучшие практики для профессионалов. — СПб.: Питер, 2021.
ISBN 978-5-4461-1587-7
© ООО Издательство "Питер", 2021
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Моей жене Норме Кастанеда
Об авторе
Фелипе Гутьеррес (Felipe Gutierrez) — архитектор ПО, получивший дипломы бакалавра и магистра в области вычислительной техники в Институте технологий и высшего образования города Монтеррей, Мексика. У Гутьерреса более 20 лет опыта в сфере IT, он разрабатывал программы для компаний из множества вертикально интегрированных отраслей, таких как государственное управление, розничная торговля, здравоохранение, образование и банковское дело. В настоящее время он работает в компании Pivotal, специализируясь на PAS и PKS для Cloud Foundry, фреймворке Spring, нативных облачных приложениях Spring, Groovy и RabbitMQ, помимо прочих технологий. Он также был архитектором ПО в таких крупных компаниях, как Nokia, Apple, Redbox и Qualcomm. Гутьеррес — автор книг Spring Boot Messaging (Apress, 2017) и Introducing Spring Framework (Apress, 2014).
О научных редакторах
Оригинальное издание
Мануэль Жордан Элера (Manuel Jordan Elera) — разработчик-самоучка и исследователь, обожает изучать новые технологии для своих экспериментов и новых их сочетаний. Мануэль получил премии Springy Award Community Champion и Spring Champion 2013. Немногое имеющееся у него свободное время он посвящает чтению Библии и сочинению музыки на гитаре. Мануэль известен под интернет-псевдонимом dr_pompeii. Он осуществлял научную редактуру многих книг, включая Pro Spring, 4-е издание (Apress, 2014)1, Practical Spring LDAP (Apress, 2013), Pro JPA 2, 2-е издание (Apress, 2013) и Pro Spring Security (Apress, 2013).
Русскоязычное издание
Валерий Алексеевич Дмитрущенков работает в IT более 35 лет. Разрабатывал программное обеспечение для множества компаний и отраслей, руководил многими комплексными проектами, включая разработку, внедрение и сопровождение автоматизированных систем. Участвовал в создании крупных проектов для государственных органов, реализованных международными организациями: ООН, USIAD, World Bank в России, Косово, Молдавии и Армении.
1 Шефер К., Хо К., Харроп Р. Spring 4 для профессионалов. 4-е изд. — М.: Вильямс, 2015.
Мануэль Жордан Элера (Manuel Jordan Elera) — разработчик-самоучка и исследователь, обожает изучать новые технологии для своих экспериментов и новых их сочетаний. Мануэль получил премии Springy Award Community Champion и Spring Champion 2013. Немногое имеющееся у него свободное время он посвящает чтению Библии и сочинению музыки на гитаре. Мануэль известен под интернет-псевдонимом dr_pompeii. Он осуществлял научную редактуру многих книг, включая Pro Spring, 4-е издание (Apress, 2014)1, Practical Spring LDAP (Apress, 2013), Pro JPA 2, 2-е издание (Apress, 2013) и Pro Spring Security (Apress, 2013).
Шефер К., Хо К., Харроп Р. Spring 4 для профессионалов. 4-е изд. — М.: Вильямс, 2015.
Благодарности
Я хотел бы выразить свою глубочайшую признательность команде издательства Apress: Стиву Энглину (Steve Anglin), принявшему мое предложение издать книгу, Марку Пауэрсу (Mark Powers) — за то, что не давал мне сбиться с пути, и за его терпение, а также остальным работникам Apress, участвовавшим в реализации данного проекта. Спасибо вам всем, что сделали это возможным.
Спасибо моему научному редактору Мануэлю Жордану за развернутость отзывов и затраченные на них усилия, а также всей команде Spring Boot за создание этой замечательной технологии.
Спасибо моим родителям Росио Круз и Фелипе Гутьерресу за их любовь и поддержку; моему брату Эдгару Герардо Гутьерресу и моей невестке Ауристелле Санчес и особенно моим девочкам, поддерживавшим меня, — Норме, Лауре, Наели и Химене, — я люблю вас, девочки. И моему сыну Родриго!
Фелипе Гутьеррес
1. Фреймворк Spring 5
Добро пожаловать в первую главу этой книги, в которой вы познакомитесь с фреймворком Spring, его историей и тем, как он развивался с момента появления. Данная глава предназначена для разработчиков, еще незнакомых со Spring. Если вы опытный разработчик на Spring, можете смело ее пропустить.
Наверное, вы думаете: «Я же хочу изучить Spring Boot. Зачем же мне знать про Spring Framework?» Позвольте поделиться секретом: Spring Boot — это Spring. У Spring Boot просто несколько иной механизм запуска приложений Spring; для полного понимания функционирования Spring Boot необходимо знать больше о фреймворке Spring.
Немного истории
Фреймворк Spring был создан в 2003 году Родом Джонсоном (Rod Johnson), автором руководства J2EE Development without EJB (издательство Wrox Publishing, 2004). Фреймворк Spring стал ответом на сложность для понимания на тот момент спецификаций J2EE. Сегодня ситуация улучшилась, но для работы определенных аспектов экосистемы J2EE необходима целая инфраструктура.
Spring можно считать дополнительной технологией для Java EE. Фреймворк Spring включает несколько технологий, таких как API сервлетов (servlet), API веб-сокетов (WebSockets), средства параллельной обработки, API привязки JSON (JSON Binding API), а также проверку достоверности данных (bean validation), JPA, JMS и JTA/JCA.
Фреймворк Spring поддерживает спецификации внедрения зависимостей (dependency injection) и стандартныханнотаций (common annotation), значительно упрощающие разработку.
В этой главе показано, что фреймворк Spring версий 5.x требует как минимум Java EE версии 7 (Servlet 3.1+ and JPA 2.1). Spring все еще работает с Tomcat 8 и 9, WebSphere 8 и JBoss EAP 7. Кроме того, я покажу вам новое расширение Spring Framework 5 — поддержку реактивности!
Сегодня Spring — один из чаще всего используемых и общепризнанных в сообществе Java фреймворков не только потому, что работает, но и потому, что продолжает внедрять различные новшества, в числе которых, помимо прочих, Spring Boot, Spring Security, Spring Data, Spring Cloud, Spring Batch и Spring Integration.
Принципы и паттерны проектирования
Чтобы разобраться в Spring Boot, необходимо изучить фреймворк; важно понимать не только на что он способен, но и то, каких принципов придерживается. Вот часть принципов фреймворка Spring.
• Возможность выбора на всех уровнях. Spring позволяет откладывать принятие проектных решений практически до самого конца. Например, можно менять поставщиков хранилищ в конфигурации без изменения кода. То же самое справедливо и относительно многих других вопросов инфраструктуры и интеграции со сторонними API. Как вам предстоит увидеть, это происходит даже на этапе развертывания приложения в облаке.
• Приспособление к различным точкам зрения. Spring приветствует гибкость и не задает никаких жестких путей решения задач. Он поддерживает широкий диапазон потребностей приложений с различными подходами.
• Обеспечение строгой обратной совместимости. Развитие Spring всегда старательно направлялось таким образом, чтобы минимизировать число изменений, нарушающих совместимость между версиями. Spring поддерживает тщательно подобранный диапазон версий JDK и сторонних библиотек, упрощая тем самым поддержку использующих его приложений и библиотек.
• Забота об архитектуре API. Команда создателей Spring вложила немало труда и размышлений в создание интуитивно понятных API, которые смогут продержаться на протяжении множества версий и лет.
• Высокие стандарты качества исходного кода. Spring Framework делает особый акцент на выразительной, актуальной и точной автодокументации (Javadocs). Это один из очень немногих проектов, которые могут по праву заявить о чистой структуре кода без циклических зависимостей между пакетами.
Итак, что же нужно для запуска приложения Spring? Spring работает с простыми Java-объектами в старом стиле (Plain Old Java Objects, POJOs), что сильно упрощает расширение. Spring обеспечивает готовность приложений к промышленной эксплуатации; впрочем, ему нужно немного помочь, добавив конфигурацию, связывающую воедино все зависимости и внедряющую зависимости, необходимые для создания компонентов Spring и выполнения приложения (рис. 1.1).
Рис. 1.1. Контекст Spring
Рисунок 1.1 демонстрирует контекст Spring, класс, создающий все компоненты Spring, — с помощью конфигурации со ссылками на все классы, благодаря чему приложение может работать. Больше подробностей вы найдете в следующих разделах, в которых мы создадим полноценное приложение с REST API.
Фреймворк Spring 5
Spring упрощает создание корпоративных приложений Java, поскольку предоставляет все необходимое разработчику для использования языка Java в корпоративной среде. В качестве альтернативных Java-языков на JVM (виртуальной машине Java) он полностью поддерживает Groovy и Kotlin.
Spring Framework 5 требует JDK 8+ и обеспечивает поддержку пакета инструментов Java Development Kit (JDK) версий 9, 10 и 11. Разработчики Spring гарантируют ту же долгосрочную техническую поддержку версий 11 и 17, что и команда создателей JDK. Эта новая версия, вышедшая в 2017 году, предлагает совершенно новый подход к функциональному программированию на основе реактивных потоков данных (Reactive Streams).
Веб-фреймворк Spring MVC был создан для обслуживания API сервлетов и сервлет-контейнеров. Пока требования к сервисам не повысились, это было нормально, но затем возникла проблема блокировки при каждом запросе; так что для большого количества запросов требовалось другое решение. В результате появился реактивный стек, веб-фреймворк. В версии 5 появился модуль Spring WebFlux с полностью неблокирующим стеком с поддержкой контроля обратного потока данных (Reactive Streams, back pressure), который работает на таких серверах, как Netty, Undertow, а также в контейнерах Servlet 3.1+. Этот неблокирующий стек поддерживает параллельное выполнение при небольшом числе потоков выполнения и способен масштабироваться при изменении аппаратных возможностей.
Модуль WebFlux зависит от другого проекта Spring: Project Reactor. Reactor представляет собой рекомендуемую реактивную библиотеку для Spring WebFlux. Он предоставляет типы API Mono и Flux для работы с последовательностями данных из 0..1 и 0..N элементов соответственно с помощью богатого набора операторов, совпадающего с перечнем операторов API ReactiveX. Reactor — библиотека, реализующая интерфейсы Reactive Streams, а потому все ее операторы поддерживают неблокирующий контроль обратного потока данных. Reactor сильно ориентирован на серверный Java и разрабатывался в тесном сотрудничестве со Spring.
Я не стану углубляться в возможности Spring, поскольку лучше показать их на примере простого веб-приложения. Не возражаете? А все эти замечательные возможности WebFlux описываются в отдельной главе2.
Простое веб-приложение Spring
Начнем с создания веб-приложения Spring — приложения ToDo, предоставляющего REST API, реализующие CRUD-операции (Create, Read, Update and Delete — «создание, чтение, обновление и удаление»). Для создания нового приложения Spring необходимо сначала установить Maven. В следующих главах мы будем использовать либо Maven, либо Cradle.
Использование Maven для создания проекта
Начнем с выполнения следующих команд из Maven для создания проекта ToDo на Spring:
$ mvn archetype:generate -DgroupId=com.apress.todo
-DartifactId=todo -Dversion=0.0.1-SNAPSHOT -DinteractiveMode=false
-DarchetypeArtifactId=maven-archetype-webapp
Эта команда генерирует основной шаблон и структуру веб-приложения. Обычно она также создает каталоги webapp и resources, но не каталог java, который придется создать вручную.
todo
├── pom.xml
└── src
└── main
├── resources
└── webapp
├── WEB-INF
│ └── web.xml
└── index.jsp
Можете импортировать этот код в свою любимую IDE; это упростит выявление каких-либо проблем.
Добавление зависимостей
Откройте файл pom.xml и замените все его содержимое листингом 1.1.
Листинг 1.1. todo/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.apress.todo</groupId>
<artifactId>todo</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>todo Webapp</name>
<properties>
<!-- Универсальные свойства -->
<java.version>1.8</java.version>
<!-- Веб -->
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>3.1.0</servlet.version>
<bootstrap.version>3.3.7</bootstrap.version>
<jackson.version>2.9.2</jackson.version>
<webjars.version>0.32</webjars.version>
<!-- Spring -->
<spring-framework.version>5.0.3.RELEASE</spring-framework.version>
<!-- JPA -->
<spring-data-jpa>1.11.4.RELEASE</spring-data-jpa>
<hibernate-jpa.version>1.0.0.Final</hibernate-jpa.version>
<hibernate.version>4.3.11.Final</hibernate.version>
<!-- Драйверы -->
<h2.version>1.4.197</h2.version>
<!-- Журналы -->
<slf4j.version>1.7.25</slf4j.version>
<logback.version>1.2.3</logback.version>
</properties>
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>${hibernate-jpa.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Журналы -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- Драйверы -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Веб-зависимости Java EE-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!-- Веб UI -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>${webjars.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${bootstrap.version}</version>
</dependency>
<!-- Веб - JSON/XML ответ -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<finalName>todo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Веб-конфигурация Spring
Начнем с конфигурации Spring. Spring требует, чтобы разработчик указал, где располагаются классы и как они взаимодействуют друг с другом, а также описал кое-какую дополнительную конфигурацию веб-приложений.
Приступим к редактированию файла web.xml, показанного в листинге 1.2.
Листинг 1.2. todo/src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>ToDo Web Application</display-name>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Необходимо задать конфигурацию класса DispatcherServlet — основной точки входа любого приложения Spring. Этот класс связывает все приложение, базируясь на конфигурации контекста. Как видите, данная конфигурация очень проста.
Создадим файл dispatcherServlet-servlet.xml для описания конфигурации контекста Spring. Существует следующее соглашение об именах: если в файле web.xml сервлет назван todo, то файл контекста Spring должен называться todo-servlet.xml. В данном случае сервлет получил название dispatcherServlet, так что Spring будет искать файл dispatcherServlet-servlet.xml (листинг 1.3).
Листинг 1.3. Файл todo/src/main/webapp/WEB-INF/dispatcherServlet-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/
spring-context-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/
jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.apress.todo" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.
MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jsonMapper"/>
</bean>
<bean class="org.springframework.http.converter.xml.
MappingJackson2XmlHttpMessageConverter">
<property name="objectMapper" ref="xmlMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="jsonMapper" class="org.springframework.http.converter.json.
Jackson2ObjectMapperFactoryBean">
<property name="simpleDateFormat" value="yyyy-MM-dd HH:mm:ss" />
</bean>
<bean id="xmlMapper" parent="jsonMapper">
<property name="createXmlMapper" value="true"/>
</bean>
<mvc:resources mapping="/webjars/**"
location="classpath:META-INF/resources/webjars/" />
<jpa:repositories base-package="com.apress.todo.repository" />
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:META-INF/sql/schema.sql" />
<jdbc:script location="classpath:META-INF/sql/data.sql" />
</jdbc:embedded-database>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.
LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.web.servlet.view.
InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="h2WebServer" class="org.h2.tools.Server"
factory-method="createWebServer"
init-method="start" destroy-method="stop">
<constructor-arg value="-web,-webAllowOthers,-webDaemon,
-webPort,8082" />
</bean>
</beans>
В листинге 1.3 приведена веб-конфигурация Spring. Рассмотрим все используемые в ней пространства имен XML. Благодаря этому при применении IDE с автодополнением кода у вас будут все компоненты и их атрибуты для каждой точки входа. Проанализируем их.
• <context:component-scan/>. Этот тег сообщает контейнеру Spring о необходимости просмотреть все классы в поиске аннотаций, включая @Service и @Configuration. Это помогает Spring в связывании воедино всех компонентов Spring, необходимых для работы приложения. В данном случае будет выполнен просмотр всех снабженных аннотациями классов на уровне пакета com.apress.todo.* и всех его подпакетов.
• <mvc:annotation-driven/>. Этот тег указывает контейнеру Spring, что речь идет о веб-приложении, так что нужно найти все классы с аннотациями @Controller и @RestController, а также их методы с аннотацией @RequestMapping или другими аннотациями Spring MVC, чтобы создать нужные MVC-компоненты для приема запросов от пользователя.
• <mvc:message-converters/>. Этот тег сообщает компонентам MVC, что следует использовать для преобразования сообщений в случае запроса. Например, при запросе с HTTP-заголовком Accept:application/xml Spring отвечает в формате XML, аналогично происходит в случае application/json.
• Компоненты jsonMapper и xmlMapper. Эти классы представляют собой компоненты Spring, которые помогают форматировать данные и создавать подходящее средство отображения (mapper).
• <mvc:resources/>. Этот тег сообщает Spring MVC, какие ресурсы использовать и где их искать. В данном случае наше приложение задействует набор библиотек WebJars (описанный в файле pom.xml).
• <jpa:repositories/>. Этот тег указывает контейнеру Spring и модулю Spring Data, где расположены интерфейсы, расширяющие интерфейс CrudRepository. В данном случае искать их нужно на уровне пакета com.apress.todo.repository.*.
• <jdbc:embedded-database/>. Поскольку наше приложение использует JPA и драйвер H2 для размещаемой в оперативной памяти базы данных, этот тег лишь описывает применение утилиты, способной выполнять при запуске приложения SQL-скрипт; в данном случае он создает таблицу todo и вставляет в нее несколько записей.
• Компонент jpaVendorAdapter. Объявление этого компонента необходимо для использования реализации JPA, в данном случае Hibernate (указанной в файле pom.xml зависимости). Другими словами, фреймворк Hibernate используется в качестве реализации API постоянного хранения объектов Java (Java Persistence API, JPA).
• Компонент EntityManagerFactory. Для каждой из реализаций JPA необходимо создать менеджер сущностей для хранения всех сеансов и выполнения всех SQL-запросов по поручению приложения.
• Компонент TransactionManager. Нашему приложению необходима транзакционность, нам ведь не нужны дубликаты или испорченные данные, правда? Необходима полная согласованность с требованиями ACID (атомарность, согласованность, изоляция и сохраняемость), так что без транзакций не обойтись.
• <tx:annotation-driven/>. Эта аннотация служит для настройки транзакций на основе предыдущих объявлений.
• Компонент viewResolver. Необходимо указать, какой тип механизма представлений будет использовать наше веб-приложение, поскольку вариантов очень много, например Java Server Faces, JSP и т.д.
• Компонент h2WebServer. Благодаря настройке с помощью этого компонента, механизма H2, к нему можно будет обращаться из приложения.
Как видите, здесь нужны определенные знания об устройстве приложений Spring. Если вы хотите разобраться в этом более детально, рекомендую заглянуть в несколько книг от издательства Apress, включая I. Cosmina Pro Spring 53.
Я хочу показать, что нужно сделать для работы простого REST API; и поверьте, если вам кажется, что это слишком сложно, то попробуйте сделать то же самое, включая все возможности нашего приложения (MVC, JPA, SQL-инициализацию, JSP, транзакции), с помощью Java EE.
Взглянем на выполняемые при запуске SQL-скрипты. Создайте следующие два файла в каталоге resources/META-INF/sql (листинги 1.4 и 1.5).
Листинг 1.4. Файл todo/src/main/resources/META-INF/sql/schema.sql
create table todo (
id varchar(36) not null,
description varchar(255) not null,
created timestamp,
modified timestamp,
completed boolean,
primary key (id)
);
Как видите, создается очень простая SQL-таблица.
Листинг 1.5. Файл todo/src/main/resources/META-INF/sql/data.sql
insert into todo values ('7fd921cfd2b64dc7b995633e8209f385','Buy
Milk','2018-09-23 15:00:01','2018-09-23 15:00:01',false);
insert into todo values ('5820a4c2abe74f409da89217bf905a0c','Read a
Book','2018-09-02 16:00:01','2018-09-02 16:00:01',false);
insert into todo values ('a44b6db26aef49e39d1b68622f55c347','Go to Spring
One 2018','2018-09-18 12:00:00','2018-09-18 12:00:00',false);
И конечно, несколько операторов вставки в таблицу todo значений.
Важно понимать, что JPA требует отдельного файла конфигурации средства постоянного хранения данных, где можно указать, например, какие управляемые контейнером классы входят в блок постоянного хранения данных, как и каким таблицам базы данных они соответствуют, подключения к источникам данных и т.д. Поэтому необходимо его создать. Создайте файл persistence.xml в каталоге resources/META-INF/ (листинг 1.6).
Листинг 1.6. Файл todo/src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="toDo">
<description>My Persistence Unit</description>
</persistence-unit>
</persistence>
Объявлять здесь соответствия классов или подключения не обязательно, поскольку эту задачу берет на себя модуль Spring Data; необходимо только объявить название persistence-unit.
Приложению обязательно необходимо журналирование, не только для отладки, но и для того, чтобы видеть, что происходит внутри приложения. Создайте файл logback.xml в каталоге resources (листинг 1.7).
Листинг 1.7. Файл todo/src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
http://ch.qos.logback/xml/ns/logback/logback.xsd">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<logger name="org.springframework" level="info" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.springframework.jdbc" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.apress.todo" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<root level="error">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Опять же ничего хитрого. Обратите внимание, что уровень журналирования для com.apress.todo установлен в значение DEBUG.
Классы
Пришло время написать собственно код для REST API приложения ToDo. Начнем с создания модели предметной области: классов предметной области ToDo. Создайте следующие классы в каталоге src/main/java.
Учтите, что утилита Maven этой структуры каталогов не создает, ее необходимо создавать вручную (листинг 1.8).
Листинг 1.8. Файл todo/src/main/java/com/apress/todo/domain/ToDo.java
package com.apress.todo.domain;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.sql.Timestamp;
@Entity
public class ToDo {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
private String description;
private Timestamp created;
private Timestamp modified;
private boolean completed;
public ToDo() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Timestamp getCreated() {
return created;
}
public void setCreated(Timestamp created) {
this.created = created;
}
public Timestamp getModified() {
return modified;
}
public void setModified(Timestamp modified) {
this.modified = modified;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}
Как видите, это обычный класс Java, но поскольку приложение предусматривает постоянное хранение данных (в нашем случае списка дел), то необходимо снабдить вышеупомянутый класс аннотацией @Entity и объявить первичный ключ (primary key) с аннотацией @Id. Этот класс также использует еще одну дополнительную аннотацию для генерации 36-символьного случайного GUID для первичного ключа.
Создадим репозиторий, обеспечивающий все CRUD-операции. В нашем случае приложение использует возможности модуля Spring Data, который скрывает весь стереотипный код для соответствия классов и таблиц и поддерживает сеансы и даже выполняет транзакции. Spring Data реализует весь набор CRUD; другими словами, вам не нужно заботиться о сохранении, обновлении, удалении и поиске записей.
Создайте интерфейс ToDoRepository, расширяющий интерфейс CrudRepository (листинг 1.9).
Листинг 1.9. Файл todo/src/main/java/com/apress/todo/repository/ToDoRepository.java
package com.apress.todo.repository;
import com.apress.todo.domain.ToDo;
import org.springframework.data.repository.CrudRepository;
public interface ToDoRepository extends CrudRepository<ToDo,String> {
}
В листинге 1.9 показан интерфейс ToDoRepository, расширяющий параметризованный интерфейс CrudRepository<T,K>. В качестве параметров CrudRepository требуются класс предметной области и тип первичного ключа; в данном случае классом предметной области служит класс ToDo, а типом первичного ключа — String (снабженный аннотацией @Id).
В XML-конфигурации использовался тег <jpa:repositories/>, который указывает на пакет ToDoRepository, означая, что Spring Data сохраняет запись и связывает воедино все относящееся к расширяющим CrudRepository интерфейсам.
Теперь создадим веб-контроллер, который будет принимать запросы от пользователей. Создайте класс ToDoController (листинг 1.10).
Листинг 1.10. Файл todo/src/main/java/com/apress/todo/controller/ToDoController.java
package com.apress.todo.controller;
import com.apress.todo.domain.ToDo;
import com.apress.todo.repository.ToDoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/")
public class ToDoController {
private ToDoRepository repository;
@Autowired
public ToDoController(ToDoRepository repository) {
this.repository = repository;
}
@GetMapping
public ModelAndView index(ModelAndView modelAndView,
HttpServletRequest request) {
modelAndView.setViewName("index");
return modelAndView;
}
@RequestMapping(value = "/toDos", method = { RequestMethod.GET },
produces = {
MediaType.APPLICATION_JSON_UTF8_VALUE,
MediaType.APPLICATION_XML_VALUE,
MediaType.TEXT_XML_VALUE})
public ResponseEntity<Iterable<ToDo>> getToDos(@RequestHeader
HttpHeaders headers) {
return new ResponseEntity<Iterable<ToDo>>(this.repository.findAll(),
headers, HttpStatus.OK);
}
}
В листинге 1.10 показан веб-контроллер. Посмотрите на этот код внимательно. Для описания всех модулей и функциональных возможностей Spring MVC нам понадобилась целая книга.
Важный нюанс: класс снабжен аннотацией @Controller. Помните тег <mv:annotation-driven/>? Он находит все помеченные аннотацией @Controller классы и регистрирует контроллеры со всеми снабженными аннотациями @GetMapping, @RequestMapping и @PostMapping методами для получения запросов в соответствии с описанными путями. В данном случае описаны только пути / и /toDos.
В качестве параметра конструктор этого класса принимает объект ToDoRepository, внедряемый контейнером Spring благодаря аннотации @Autowired. Эту аннотацию можно опустить при использовании Spring версии 4.3; в ней по умолчанию контейнер Spring определяет нужные конструктору зависимости и внедряет их автоматически. Это все равно что сказать контейнеру Spring: «Эй, контейнер Spring, я собираюсь использовать компонент ToDoRepository, внедри его, пожалуйста». Именно так Spring применяет внедрение зависимостей (существует также внедрение методов, внедрение полей и внедрение сеттеров).
Аннотация @GetMapping (@RequestMapping по умолчанию делает то же самое) задает обработчик для пути / и имя представления; в данном случае она возвращает соответствующее JSP-странице WEB-INF/view/index.jsp имя index. Аннотация @RequestMapping — еще один способ сделать то же самое (что и @GetMapping), но она объявляет путь /toDos. Получаемый от этого метода ответ зависит от типа заголовка, отправленного инициатором запроса, например application/json или application/xml. В качестве ответа используется объект ResponseEntity; для вызова метода findAll (возвращающего все запланированные дела (ToDo)) используется экземпляр репозитория, поскольку в конфигурации средств отображения для типов содержимого JSON и XML объявлено, что это преобразование берет на себя движок.
Опять же не торопитесь и внимательно проанализируйте происходящее. После запуска приложения вы сможете поэкспериментировать со всеми этими аннотациями.
Создадим представление, являющее собой JSP-страницу, вызываемую при обращении к пути /. Создайте файл index.jsp в каталоге WEB-INF/views (листинг 1.11).
Листинг 1.11. Файл todo/src/main/webapp/WEB-INF/views/index.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple Directory Web App</title>
<link rel="stylesheet" type="text/css"
href="webjars/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css"
href="webjars/bootstrap/3.3.7/css/bootstrap-theme.min.css">
</head>
<body>
<div class="container theme-showcase" role="main">
<div class="jumbotron">
<h1>ToDo Application</h1>
<p>A simple Rest API Spring MVC application</p>
</div>
<div class="page-header">
<h1>API</h1>
<a href="toDos">Current ToDos</a>
</div>
</div>
</body>
</html>
Единственное, на что стоит здесь обратить внимание, — использование ресурсов, например набора библиотек WebJars. Приложение задействует CSS Bootstrap. Но откуда эти ресурсы берутся? Во-первых, в файле pom.xml объявлена зависимость org.webjars:bootstrap. Во-вторых, в конфигурации использовался тег <mvc:resources/>, указывающий, где эти ресурсы искать.
Запуск приложения
Мы завершили написание конфигурации и необходимого для работы приложения кода. Настало время сервера приложений. Для запуска приложения следуйте инструкции.
1. Откройте терминал и перейдите в корневой каталог проекта (todo/). Выполните следующую команду Maven:
$ mvn clean package
Эта команда упакует наше приложение в WAR-файл (веб-архив), готовый для развертывания на сервере приложений. Этот файл будет располагаться в каталоге target/ и называться todo.war.
2. Скачайте сервер приложений Tomcat (для работы этого приложения не нужны тяжеловесные серверы приложений; вполне достаточно облегченного Tomcat). Скачать его можно по адресу: https://tomcat.apache.org/download-90.cgi.
3. Распакуйте его и установите в любой каталог.
4. Скопируйте файл target/todo.war в каталог <tomcat-installation>/webapps/.
5. Запустите Tomcat. Запустите браузер и перейдите по адресу http://localhost:8080/todo (рис. 1.2).
Рис. 1.2.http://localhost:8080/todo/
Если вы щелкнете на ссылке, то получите XML-ответ (рис. 1.3).
Рис. 1.3.http://localhost:8080/todo/toDos
Как получить JSON-ответ? Откройте терминал и выполните следующую команду.
$ curl -H "Accept: application/json" localhost:8080/todo/toDos
[ {
"id" : "7fd921cfd2b64dc7b995633e8209f385",
"description" : "Buy Milk",
"created" : "2018-09-23 15:00:01",
"modified" : "2018-09-23 15:00:01",
"completed" : false
}, {
"id" : "5820a4c2abe74f409da89217bf905a0c",
"description" : "Read a Book",
"created" : "2018-09-02 16:00:01",
"modified" : "2018-09-02 16:00:01",
"completed" : false
}, {
"id" : "a44b6db26aef49e39d1b68622f55c347",
"description" : "Go to Spring One 2018",
"created" : "2018-09-18 12:00:00",
"modified" : "2018-09-18 12:00:00",
"completed" : false
} ]
Можете проверить с application/xml — вы увидите тот же результат, что и в браузере. Поздравляю! Вы только что создали свое первое приложение MVC Spring с REST API.
Использование Java-конфигурации
Возможно, XML представляется вам слишком «многословным» способом описания конфигурации. Что ж, иногда так и есть, но в Spring имеется и другой способ задания настроек контейнера Spring — с помощью аннотаций и классов конфигурации на языке Java.
Если хотите попробовать этот способ, создайте класс ToDoConfig и вставьте в него код, приведенный в листинге 1.12.
Листинг 1.12. Файл todo/src/main/java/com/apress/todo/config/ToDoConfig.java
package com.apress.todo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.
MappingJackson2XmlHttpMessageConverter;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.
ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import javax.sql.DataSource;
import java.text.SimpleDateFormat;
import java.util.List;
@Configuration
@EnableJpaRepositories(basePackages="com.apress.todo.repository")
@EnableTransactionManagement
@EnableWebMvc
public class ToDoConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/","/
resources/","/webjars/")
.resourceChain(true).addResolver(new WebJarsResourceResolver());
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>>
converters) {
Jackson2ObjectMapperBuilder builder = new
Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new
SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
converters.add(new MappingJackson2HttpMessageConverter(
builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(
builder.createXmlMapper(true).build()));
}
@Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
return bean;
}
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder =
new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).addScript("META-INF/
sql/schema.sql").addScript("META-INF/sql/data.sql").build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter =
new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory =
new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().
getNativeEntityManagerFactory());
return txManager;
}
}
Листинг 1.12, по сути, то же самое, что XML-конфигурация, только теперь используется класс конфигурации на языке Java, в котором мы объявляем компоненты Spring программным образом, переопределяя часть веб-конфигурации.
Если хотите проверить его на деле, необходимо кое-что сделать. Откройте файл dispatcherServlet-servlet.xml, который должен выглядеть так:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beanshttp://www.
springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.
springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.apress.todo" />
</beans>
В конце этого файла необходимо сообщить Spring, где искать класс, снабженный аннотацией @Configuration (в качестве альтернативы можно использовать класс WebApplicationInitializer); когда Spring найдет его, тогда сможет связать приложение воедино на основе объявлений в классе Java-конфигурации.
Не забудьте удалить и заново упаковать свое приложение с помощью команды mvncleanpackage для повторной генерации WAR-файла. Можете запустить его. Вы должны получить такой же результат, что и при использовании XML-конфигурации.
Итак, как вам фреймворк Spring? Да, конечно, нужно разобраться, что происходит. Необходимо понять, как выглядит жизненный цикл компонентов Spring и как применяется внедрение зависимостей. Важно также иметь хотя бы небольшое представление об AOP (аспектно-ориентированном программировании), поскольку в том числе и благодаря ему связывается воедино работающее приложение Spring.
Не слишком ли много всего? Что ж, попробуйте создать точно такое же приложение с помощью обычного J2EE — и усилий на это придется потратить еще больше. Помните, оно не только предоставляет REST API, но и работает с базой данных, транзакциями, конвертерами сообщений, распознаванием представлений и многим другим; именно поэтому веб-приложения легче создавать с помощью Spring.
Но знаете что? Spring Boot предоставляет все шаблоны конфигурации, что еще более ускоряет разработку корпоративных приложений Spring!
Примечание
Исходный код для этой книги можно найти на сайте Apress или в GitHub по адресу https://github.com/Apress/pro-spring-boot-2.
Резюме
Говорить о фреймворке Spring и роли Spring Boot можно очень долго. Одной главы, конечно, недостаточно. Так что, если хотите узнать больше, рекомендую заглянуть в документацию Spring по адресу https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/.
В следующей главе мы приступим к работе со Spring Boot и увидим, насколько легко можно создать то же самое приложение, но «а-ля Boot».
Как видите, здесь нужны определенные знания об устройстве приложений Spring. Если вы хотите разобраться в этом более детально, рекомендую заглянуть в несколько книг от издательства Apress, включая I. Cosmina Pro Spring 53.
Я не стану углубляться в возможности Spring, поскольку лучше показать их на примере простого веб-приложения. Не возражаете? А все эти замечательные возможности WebFlux описываются в отдельной главе2.
В главе 6. — Здесь и далее примеч. пер.
Козмина Ю.и др. Spring 5 для профессионалов. — М.: Диалектика-Вильямс, 2019.
2. Введение в Spring Boot
В предыдущей главе я показал вам, что такое фреймворк Spring, часть основных его возможностей (в частности, реализацию паттерна проектирования «Внедрение зависимостей»), а также продемонстрировал его использование на примере создания простого веб-приложения и его развертывания на сервере Tomcat. Мы также проследили все шаги создания приложения Spring (например, параметры конфигурации с добавлением различных XML-файлов и способ запуска приложения).
В этой главе я покажу вам, что такое Spring Boot — его основные компоненты, как создавать, запускать и развертывать приложения Spring с его помощью. Spring Boot упрощает создание приложений Spring. Подробности описаны в оставшейся части данной книги, здесь вас ждет только небольшое введение в технологию Spring Boot.
Spring Boot
Можно сказать, что Spring Boot — следующий этап эволюции фреймворка Spring, но не поймите меня превратно: Spring Boot — вовсе не замена фреймворку Spring, поскольку Spring Boot и есть фреймворк Spring! Spring Boot можно рассматривать как новый способ простого и удобного создания приложений Spring.
Spring Boot упрощает способ разработки, поскольку позволяет легко создавать готовые к промышленной эксплуатации приложения на основе Spring, которые можно просто «запустить». Вы скоро узнаете, что с помощью Spring Boot можно создавать автономные приложения со встроенным сервером (по умолчанию Tomcat или Netty в случае использования новых веб-реактивных модулей), полностью готовые к запуску и развертыванию. Мы поговорим больше про это в нескольких из последующих глав данной книги.
Одна из важнейших возможностей Spring Boot — продуманная среда выполнения, благодаря которой разработчику легче следовать рекомендуемым практикам по созданию надежных, расширяемых и масштабируемых приложений Spring.
Проект Spring Boot можно найти по адресу https://projects.spring.io/spring-boot/. А чрезвычайно подробную документацию — вот здесь: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/. Домашняя страница Spring Boot показана на рис. 2.1.
Рис. 2.1. Домашняя страница Spring Boot (https://projects.spring.io/spring-boot/)
Spring Boot спешит на помощь
Для создания приложений Spring необходимо знать все приемы настроек и/или требования данной технологии. Для запуска даже простейшего приложения Spring необходимо пройти длинный путь. Четыре года назад команда создателей Spring выпустила первую бета-версию Spring Boot, которую мне посчастливилось тестировать. Результаты были потрясающими. Теперь, после добавления в эту технологию множества новых возможностей, она и правда стала де-факто основным способом создания приложений Spring. Spring Boot существенно упрощает создание готовых к промышленной эксплуатации приложений.
На веб-странице проекта Spring Boot можно найти следующее утверждение: Абсолютно никакой генерации кода и необходимости в XML-конфигурации. Наверное, вы недоумеваете: как можно создавать приложения Spring и как они могут работать без какой-либо конфигурации? Контейнеру Spring нужно по крайней мере знать, как связать воедино классы, правда? А еще ему нужно знать, как использовать добавленные вами в приложение технологии. Что ж, не беспокойтесь. Я расскажу вам все секреты этой замечательной технологии. Но сначала создадим простейшее из возможных приложений Spring (листинг 2.1).
Листинг 2.1. app.groovy
@RestController
class WebApp{
@GetMapping("/")
String welcome(){
"<h1><font face='verdana'>Spring Boot Rocks!</font></h1>"
}
}
В листинге 2.1 приведено приложение Groovy — простейшее из возможных приложений Spring. Почему Groovy? Я всегда говорю студентам: если вы знаете Java, значит, знаете и Groovy. Groovy устраняет весь стереотипный код Java и позволяет создавать веб-приложения с помощью всего нескольких строк кода (но не волнуйтесь, эта книга в основном касается Java, лишь в последней главе мы поговорим о Groovy и Kotlin, новое прибавление к списку поддерживаемых Spring языков). Как же его запустить? Очень просто, всего лишь выполнить команду:
$ spring run app.groovy
После этого вы увидите вывод в консоль журнала с баннером Spring Boot, инициализацией контейнера Tomcat и примечание, что приложение было запущено на порте 8080. Если после этого открыть браузер и ввести http://localhost:8080, вы увидите текст Spring Boot Rocks!.
Наверное, вы воскликнете: «Погодите! Что за команда springrun? Как ее установить? Что еще мне нужно? Это и есть Spring Boot?» Ну, она представляет собой один из многих способов создания и запуска приложения Spring. Это была одна из первых попыток продемонстрировать мощь данной технологии (четыре года назад), простой сценарий, способный запустить полноценное веб-приложение Spring. Команда Spring Boot создала интерфейс командной строки Spring Boot (Spring Boot CLI).
Spring Boot CLI
Spring Boot CLI (интерфейс командной строки) — один из многих способов создания приложений Spring, впрочем обычно используемый для прототипов приложений. Можете считать его «песочницей» Spring Boot. Эталонная его модель описана в следующих разделах. Я просто хотел, чтобы вы попробовали на вкус немножко возможностей Spring с помощью простых скриптов Groovy или Java. Лично для меня интерфейс командной строки Spring — неотъемлемая составная часть экосистемы Spring Boot.
Вернемся к предыдущему коду. Обратили ли вы внимание, что в листинге 2.1 нет операторов импорта? Откуда же Spring Boot знает про веб-приложение вообще и как его запустить в частности?
Spring Boot CLI изучает код и пытается на основе аннотаций Spring MVC (@RestController и @GetMapping) выполнить его как веб-приложение, используя встроенный сервер Tomcat и запуская это веб-приложение на нем. Скрытая за кулисами магия состоит в том, что язык программирования Groovy обеспечивает простой способ перехвата операторов и динамического создания кода с помощью абстрактного синтаксического дерева (abstract syntax tree, AST); благодаря этому можно без проблем внедрить недостающий код Spring и выполнить его. Другими словами, Spring Boot CLI выясняет всю нужную информацию о вашем приложении и внедряет недостающие для создания и выполнения полноценного веб-приложения Spring части.
Помните, я упоминал, что он может также выполнять сценарии Java? Взглянем на Java-версию того же приложения. Пока что я просто покажу вам код; если хотите выполнять эти приложения, загляните в приложение, где объясняется, как установить Spring Boot CLI, и описываются его возможности (листинг 2.2).
Листинг 2.2. SimpleWebApp.java
package com.apress.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SimpleWebApp {
public static void main(String[] args) {
SpringApplication.run(SimpleWebApp.class, args);
}
@RequestMapping("/")
public String greetings(){
return "<h1>Spring Boot Rocks in Java too!</h1>";
}
}
В листинге 2.2 показана точка входа для приложения Spring Boot на Java. Прежде всего в ней используются аннотация @SpringBootApplication и класс-одиночка (singleton) SpringApplication в методе main, который и выполняет приложение. Метод SpringApplication.run принимает два параметра. Первый параметр — основной класс конфигурации, содержащий аннотацию @Configuration (причем имя класса совпадает; но об этом позже). Второй параметр представляет собой аргументы приложения (о которых мы поговорим в следующих главах). Как вы видите из этой Java-версии, мы используем аннотации Spring MVC: @RestController и @GetMapping.
Запустить этот пример на выполнение можно с помощью команды:
$ spring run SimpleWebApp.java
Если после этого вы откроете браузер и перейдете по адресу http://localhost:8080, то увидите текст Spring Boot Rocks in Java too!.
Если вы хотите установить свой Spring Boot CLI, можете перейти к приложению данной книги, в котором пошагово описываются установка, все возможности и преимущества Spring Boot CLI. Spring Boot CLI идеально подходит для быстрого создания прототипов облачных приложений Spring; именно поэтому я решил включить рассказ о нем в эту книгу.
Модель приложения Spring Boot
Spring Boot задает способ простого создания приложений Spring и модель программирования, следующую рекомендуемым практикам приложений Spring. Для создания приложения Spring необходимы следующие компоненты.
• Утилита сборки/управления зависимостями, например Maven или Gradle (Spring Boot также поддерживает Ant и Ivy; в этой книге для всех примеров достаточно Maven или Gradle).
• Корректное управление зависимостями и соответствующие плагины в утилите сборки. При работе с Maven требуется тег <parent/> (конечно, существуют и другие способы настройки Spring Boot, но использование тега <parent/> — простейший) и плагин spring-boot-maven-plugin. Если же вы используете Gradle, вам понадобятся плагины org.springframework.boot и io.spring.dependency-management.
Нужно также добавить необходимые зависимости с помощью spring-boot-starters и создать основной класс приложения, включающий:
• аннотацию @SpringBootApplication;
• оператор SpringApplication.run в методе main.
В следующем разделе мы собираемся создать наше первое приложение Spring Boot, и я дам подробные объяснения насчет всех вышеупомянутых компонентов. Все довольно просто, но с чего же начать? Существует ли утилита, с помощью которой можно начать проект Spring Boot? Ответ: да! Можно воспользоваться Spring Boot CLI, с помощью которого можно создавать проекты Spring Boot. Существуют также IDE (интерактивные среды разработки), например STS (Spring Tool Suite — «комплекс инструментов для Spring», https://spring.io/tools), IntelliJ IDEA от JetBrains (https://www.jetbrains.com/idea/), NetBeans (https://netbeans.org), Atom от GitHub (https://atom.io) и VSCode компании Microsoft (https://code.visualstudio.com). У Atom и VSCode есть плагины для упрощения разработки приложений Spring, но лично я, начиная проект Spring Boot, предпочитаю Spring Initializr (http://start.spring.io). В этой книге задействуется IntelliJ IDEA.
Взглянем, как использовать веб-сервис S
