Быстрая разработка современного приложения: Spring Roo

Все мы, включая программистов, мечтаем о скатерти-самобранке: ей говоришь, что нам надо, а она делает. Скатерть в сказке работает быстро. Это само собой разумеется. И как она это делает, нам знать не нужно.

Если сказку сделать былью в современной разработке, то хотелось бы иметь возможность создавать веб-приложения, даже не зная фронтэнд. И ещё: современное приложение – это мониторинг, безопасность, логирование, полнотекстовой поиск и многое ещё – сто одёжек с застежками и без. А собственно бизнес-логики в приложении зачастую – совсем не много. Львиную долю времени разработчика сжирает всё остальное. И хочется, чтобы и тут нам наша скатерть помогла.

Подобные программы (по-английски – Rapid Application Development) делались, делаются и будут делаться. Не избежал своей попытки и стек Spring. Называется программа – Spring Roo. Появился Spring Roo 10 лет назад и первое время активно развивался. Выходили книги – н-р, «Spring Roo in Action». Но потом разработчики Pivotal решили сконцентрироваться на Spring Boot, который стал сверхуспешным и вдохнул в Spring новую жизнь. А разработку Spring Roo полностью отдали на откуп испанской фирме DISID. С тех пор проект развивается неспешно. Вышла вторая версия, несовместимая с первой. В ней появились Spring Boot и Thymleaf с Freemarker, но и пропали некоторые фишки первой версии вроде возможности автоматического создания классов и их связей по schema.sql.

У Spring Roo на гитхабе – 632 звезды (для сравнения, у идейного наследника Spring Roo – JHipster – 13 494). Примеров его сопряжения с различными иными технологиями не слишком много. Да и вопросов и ответов по Spring Roo не накоплено в достаточном количестве.

С другой стороны, Spring Roo – это по-прежнему очень мощный и полезный инструмент. Особенно для создания CRUD-приложений или CRUD-частей сложных приложений (отдельный user management, например).

Spring Roo создаёт удобный и многофункциональный пользовательский интерфейс с поиском «из коробки». Разработка осуществляется чрезвычайно быстро. И кто знает – возможно, Spring Roo получит новое развитие и популярность. И может быть, этому в будущем поспособствуете именно вы.

Как пользоваться Spring Roo?

А пока давайте рассмотрим, как им пользоваться. В этом нет никакой сложности. Для начала вы скачиваете по адресу https://projects.spring.io/spring-roo/ zip-file (http://spring-roo-repository.springsource.org.s3.amazonaws.com/release/ROO/spring-roo-2.0.0.RELEASE.zip), распаковываете содержимое в произвольный каталог – вот и вся инсталляция.

Остаётся только прописать путь к программе в системной переменной Path. Например: C:\Program Files\spring-roo-2.0.0.RELEASE\bin;

Теперь можно создать папку для будущей программы, которую для нас разработает Spring Roo, и запустить Spring Roo оттуда в командной строке командой roo. На экране мы увидим нечто подобное:

                _
 ___ _ __  _ __(_)_ __   __ _   _ __ ___   ___
/ __| '_ \| '__| | '_ \ / _` | | '__/ _ \ / _ \
\__ \ |_) | |  | | | | | (_| | | | | (_) | (_) |
|___/ .__/|_|  |_|_| |_|\__, | |_|  \___/ \___/
    |_|                 |___/          2.0.0.RC1

Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.

Всё. Теперь мы уже внутри Roo и из командной строки может запускать его команды. К примеру, help или hint.

roo> hint 
Roo requires the installation of a persistence configuration.

Type 'jpa setup' and then hit CTRL+SPACE. We suggest you type 'H'
then CTRL+SPACE to complete "HIBERNATE".

After the --provider, press CTRL+SPACE for database choices.
For testing purposes, type (or CTRL+SPACE) HYPERSONIC_IN_MEMORY.
If you press CTRL+SPACE again, you'll see there are no more options.
As such, you're ready to press ENTER to execute the command.

Once JPA is installed, type 'hint' and ENTER for the next suggestion.
roo>

Подсказки Roo выдаёт в зависимости от контекста. А ещё, если нажимать клавишу Tab, Roo сам предложит доступную команду, опцию или имя скрипта, а также автоматически дополнит введённое вами.

Особенности работы со Spring Roo

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

Вместо того, чтобы по очереди вводить команды, имеет смысл просто записать их все в один текстовый файл, а потом командой script исполнить их все сразу. С учётом того, что все введённые нами команды Roo записывает в log-файл, создание скрипта обычно представляет собой копирование и творческое редактирование этого самого лога.

Соответственно, если вы в какой-то из команд совершили ошибку и хотите исправить получившийся результат, вы можете не только повторно ввести команду с опцией –force и откорректированными значениями, но и просто поменять нужное место в скрипте и запустить его полностью по новой, предварительно уничтожив весь код с ошибкой.

Вы также можете параллельно с командной строкой открыть генерируемый Roo код в вашей IDE. STS поддерживает Spring Roo с помощью соответствующего плагина, в нём вам не нужен отдельный shell. IDEA поддерживала Roo с версии 11 по 15. Но плагин совершенно не обязателен. Совершённые вами в IDE изменения автоматически подхватятся Roo в shell и отразятся там.

Под капотом у Spring Roo – AspectJ. И значительная часть кода содержится в .ITD-файлах (AspectJ inter-type declaration). С помощью команды push-in вы можете перенести содержимое этих файлов в файлы java. Однако после этого внесённые вами в код изменения Roo больше не распознает.

Подробнее о командах и опциях Spring Roo вы можете прочитать здесь.

Создаём онлайн-магазин

Давайте мы уже, наконец, рассмотрим пример. Создадим онлайн-магазин (для создания скрипта под запуск просто уберите из текста ниже все комментарии):

// Create project

project setup --topLevelPackage com.disid.restful --projectName restfulshop



// Setup JPA

jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY



// Create entities without relations



// Address

entity jpa --class ~.model.Address

field string --fieldName street

field string --fieldName city

field number --fieldName streetNumber --type Integer --min 1



// Category

entity jpa --class ~.model.Category --entityFormatMessage category_format

field string --fieldName name --sizeMin 3 --sizeMax 30

field string --fieldName description



// Customer

entity jpa --class ~.model.Customer --entityFormatExpression "#{firstName} #{lastName}"

field string --fieldName firstName

field string --fieldName lastName



// Product

entity jpa --class ~.model.Product --entityFormatExpression #{name}

field string --fieldName name

field string --fieldName description



// CustomerOrder

entity jpa --class ~.model.CustomerOrder --entityFormatExpression #{shipAddress}

field date --fieldName orderDate --type java.util.Date --dateTimeFormatPattern "dd/MM/yyyy"

field date --fieldName shippedDate --type java.util.Date --dateTimeFormatPattern "dd/MM/yyyy"

field string --fieldName shipAddress



// OrderDetailPK

//embeddable --class ~.model.OrderDetailPK --serializable

//field number --fieldName id --type Integer

//field number --fieldName customerOrderId --type Long --column customerOrderId



// OrderDetail

//entity jpa --class ~.model.OrderDetail --identifierType ~.model.OrderDetailPK

entity jpa --class ~.model.OrderDetail

field number --fieldName quantity --type Integer



// Customer relations

focus --class ~.model.Customer

field reference --fieldName address --type ~.model.Address --aggregation false --joinColumnName my_customer

field set --fieldName orders --type ~.model.CustomerOrder --joinTable my_customer_orders --joinColumns my_order --referencedColumns id --inverseJoinColumns my_customer --inverseReferencedColumns id



// Category relations

focus --class ~.model.Category

field set --fieldName products --type ~.model.Product --cardinality MANY_TO_MANY --aggregation --joinTable my_products_categories --joinColumns my_product --referencedColumns id --inverseJoinColumns my_category --inverseReferencedColumns id



// CostumerOrder relations

focus --class ~.model.CustomerOrder

field set --fieldName details --type ~.model.OrderDetail --cardinality ONE_TO_MANY --aggregation false



// Product relations

focus --class ~.model.Product

field set --fieldName orderDetails --type ~.model.OrderDetail



// Generate repositories

repository jpa --all



// Generate service layer

service --all



// Generate finders

dto --class ~.model.CustomerFindByFirstNameAndLastName

field string --fieldName firstName

field string --fieldName lastName

finder add --entity ~.model.Customer --name findByFirstNameAndLastName --formBean ~.model.CustomerFindByFirstNameAndLastName



// Generate web layer

web mvc setup

web mvc language --code es

web mvc controller --all

web mvc view setup --type THYMELEAF

web mvc controller --all --responseType THYMELEAF



// Publishing finders in web layer

web mvc finder --entity ~.model.Customer

web mvc finder --entity ~.model.Customer --responseType THYMELEAF





// detail OneToMany aggregation

web mvc detail --entity ~.model.Customer --field orders

web mvc detail --entity ~.model.Customer --field orders --responseType THYMELEAF --views list,findByFirstNameAndLastName



// detail ManyToMany aggregation

web mvc detail --entity ~.model.Category --field products

web mvc detail --entity ~.model.Category --field products --responseType THYMELEAF



// detail OneToMany composition

web mvc detail --entity ~.model.CustomerOrder --field details

web mvc detail --entity ~.model.CustomerOrder --field details --responseType THYMELEAF



// Unit tests

test unit --class ~.model.Product

test unit --class ~.model.CustomerOrder

test unit --class ~.model.Address



// Integration tests

test integration --class ~.repository.CustomerRepository

test integration --class ~.repository.CategoryRepository

test integration --class ~.web.CustomerOrdersCollectionThymeleafController

test integration --class ~.web.CustomerOrdersCollectionJsonController

После того, как скрипт выше отработал (и Spring Roo создал весь необходимый код), можно выйти из Roo (команда quit) и запустить полученное приложение – mvn spring-boot:run

Остаётся открыть магазин в браузере: http://localhost:8080/

Ниже – несколько скриншотов магазина.

Остаётся пожелать вам успешной работы со Spring Roo. И не забудьте поделиться с другими вашими достижениями на этой ниве!