1. Обзор
В настоящее время существует множество сред на основе JEE, таких как Spring , Play и Grails , доступных для разработки веб-приложений.
У нас могут быть причины предпочесть один из них другим. Однако наш выбор также зависит от варианта использования и проблемы, которую мы пытаемся решить.
В этом вводном руководстве мы изучим веб-фреймворк Ninja и создадим простое веб-приложение. В то же время мы рассмотрим несколько основных функций, которые он предоставляет.
2. Ниндзя
Ninja — это полнофункциональная, но легкая веб-инфраструктура, которая использует существующие библиотеки Java для выполнения своей работы.
Имея функции от HTML до рендеринга JSON, устойчивости к тестированию, это универсальное решение для создания масштабируемых веб-приложений.
Он следует парадигме « конвенция над конфигурацией » и классифицирует код по пакетам, таким как модели
, контроллеры
и службы
.
Ninja использует популярные библиотеки Java для ключевых функций, таких как Jackson для рендеринга JSON/XML, Guice для управления зависимостями, Hibernate для сохраняемости и Flyway для миграции баз данных .
Для быстрой разработки он предлагает SuperDevMode для горячей перезагрузки кода. Таким образом, это позволяет нам мгновенно видеть изменения в среде разработки.
3. Настройка
Ninja требует стандартный набор инструментов для создания веб-приложения:
- Java 1.8 или новее
- Maven 3 или более поздней версии
- IDE (Eclipse или IntelliJ)
Мы будем использовать архетип Maven для быстрой настройки проекта Ninja. Нам будет предложено указать идентификатор группы, идентификатор артефакта и номер версии, а затем имя проекта:
mvn archetype:generate -DarchetypeGroupId=org.ninjaframework \
-DarchetypeArtifactId=ninja-servlet-archetype-simple
Или, для существующего проекта Maven, мы можем добавить последнюю зависимость ninja-core в pom.xml
:
<dependency>
<groupId>org.ninjaframework</groupId>
<artifactId>ninja-core</artifactId>
<version>6.5.0</version>
</dependency>
Затем мы запустим команду Maven для первой компиляции файлов:
mvn clean install
Наконец, давайте запустим приложение с помощью предоставленной Ninja команды Maven:
mvn ninja:run
Вуаля! Наше приложение запущено и будет доступно по адресу localhost:8080
:
4. Структура проекта
Давайте взглянем на Maven-подобную структуру проекта, созданную Ninja:
Фреймворк создает несколько пакетов на основе соглашений.
**Классы Java распределены по категориям в каталогах conf
, controllers
, models
и services
в src/main/java.
**
Аналогично, src/test/java
содержит соответствующие классы модульных тестов.
Каталог представлений
в src/main/java
содержит файлы HTML. Кроме того, каталог src/main/java/assets
содержит такие ресурсы, как изображения, таблицы стилей и файлы JavaScript.
5. Контроллер
Мы готовы обсудить несколько основных особенностей фреймворка. Контроллер — это класс, который получает запрос и возвращает ответ с определенными результатами.
Во-первых, давайте обсудим несколько соглашений, которым нужно следовать:
- Создайте класс в пакете
контроллеров
и добавьте к имени суффиксController .
- Метод, обслуживающий запрос, должен возвращать объект класса
Result .
Давайте создадим класс ApplicationController
с простым методом для рендеринга HTML:
@Singleton
public class ApplicationController {
public Result index() {
return Results.html();
}
}
Здесь метод index
будет отображать HTML, вызывая метод html класса
Results
. Объект Result
содержит все, что требуется для отображения содержимого, например код ответа, заголовки и файлы cookie.
Примечание. Аннотация Guice @Singleton
допускает использование только одного экземпляра контроллера в приложении .
6. Просмотр
Для метода index
Ninja будет искать файл HTML — index
.ftl.html
в каталоге views/ApplicationController
.
Ninja использует механизм шаблонов Freemarker для рендеринга HTML . Таким образом, все файлы в представлениях
должны иметь расширение .ftl.html
.
Давайте создадим файл
индекса
.ftl.html
для метода индекса
:
``
<html>
<head>
<title>Ninja: Index</title>
</head>
<body>
<h1>${i18n("helloMsg")}</h1>
<a href="/userJson">User Json</a>
</body>
</html>
Здесь мы использовали предоставленный Ninja тег i18n для получения свойства
helloMsg
из файла message.properties .
Мы обсудим это позже в разделе интернационализации.
7. Маршрут
Далее мы определим маршрут, по которому запрос достигнет метода индекса
.
Ninja использует класс Routes в пакете
conf
для сопоставления URL-адреса с определенным методом контроллера.
Давайте добавим маршрут для доступа к методу index
ApplicationController
:
public class Routes implements ApplicationRoutes {
@Override
public void init(Router router) {
router.GET().route("/index").with(ApplicationController::index);
}
}
Вот и все! Все готово для доступа к индексной
странице по адресу localhost:8080/index
:
8. JSON-рендеринг
Как уже говорилось, Ninja использует Jackson для рендеринга JSON. Чтобы отобразить содержимое JSON, мы можем использовать метод json класса
Results
.
Давайте добавим метод userJson
в класс ApplicationController
и отобразим содержимое простого HashMap
в JSON:
public Result userJson() {
HashMap<String, String> userMap = new HashMap<>();
userMap.put("name", "Norman Lewis");
userMap.put("email", "norman@email.com");
return Results.json().render(user);
}
Затем мы добавим необходимую маршрутизацию для доступа к userJson
:
router.GET().route("/userJson").with(ApplicationController::userJson);
Теперь мы можем отображать JSON, используя localhost:8080/userJson
:
9. Сервис
Мы можем создать службу, чтобы отделить бизнес-логику от контроллера и внедрять нашу службу везде, где это необходимо.
Во-первых, давайте создадим простой интерфейс UserService
для определения абстракции:
public interface UserService {
HashMap<String, String> getUserMap();
}
Затем мы реализуем интерфейс UserService в классе
UserServiceImpl
и переопределим метод getUserMap
:
public class UserServiceImpl implements UserService {
@Override
public HashMap<String, String> getUserMap() {
HashMap<String, String> userMap = new HashMap<>();
userMap.put("name", "Norman Lewis");
userMap.put("email", "norman@email.com");
return userMap;
}
}
Затем мы свяжем интерфейс UserService с классом
UserServiceImpl
, используя функцию внедрения зависимостей Ninja, предоставляемую Guice.
Добавим привязку в класс Module
, доступный в пакете conf :
@Singleton
public class Module extends AbstractModule {
protected void configure() {
bind(UserService.class).to(UserServiceImpl.class);
}
}
Наконец, мы добавим зависимость UserService
в класс ApplicationController , используя аннотацию
@Inject
:
public class ApplicationController {
@Inject
UserService userService;
// ...
}
Таким образом, мы готовы использовать метод
getUserMap
UserService в ApplicationController
:
public Result userJson() {
HashMap<String, String> userMap = userService.getUserMap();
return Results.json().render(userMap);
}
10. Флэш-прицел
Ninja предоставляет простой, но эффективный способ обработки сообщений об успехах и ошибках в запросах с помощью функции под названием Flash Scope.
Чтобы использовать его в контроллере, мы добавим в метод аргумент FlashScope :
public Result showFlashMsg(FlashScope flashScope) {
flashScope.success("Success message");
flashScope.error("Error message");
return Results.redirect("/home");
}
Примечание. Метод перенаправления
класса Results
перенаправляет цель на предоставленный URL-адрес.
Затем мы добавим маршрутизацию /flash
в метод showFlashMsg
и изменим представление для отображения флэш-сообщений:
<#if (flash.error)??>
<div class="alert alert-danger">
${flash.error}
</div>
</#if>
<#if (flash.success)??>
<div class="alert alert-success">
${flash.success}
</div>
</#if>
Теперь мы можем увидеть FlashScope
в действии по адресу localhost:8080/flash
:
11. Интернационализация
Ninja предоставляет встроенную функцию интернационализации, которую легко настроить.
Во-первых, мы определим список поддерживаемых языков в файле application.conf :
application.languages=fr,en
Затем мы создадим файл свойств по умолчанию — messages.properties
для английского языка — с парами ключ-значение для сообщений:
header.home=Home!
helloMsg=Hello, welcome to Ninja Framework!
Точно так же мы можем добавить код языка в имя файла для файла свойств для конкретного языка — например, файл message_fr.properties
для французского языка:
header.home=Accueil!
helloMsg=Bonjour, bienvenue dans Ninja Framework!
Когда конфигурации готовы, мы можем легко включить интернационализацию в классе ApplicationController
.
У нас есть два способа: либо с помощью класса Lang , либо с помощью класса
Messages
:
@Singleton
public class ApplicationController {
@Inject
Lang lang;
@Inject
Messages msg;
// ...
}
Затем с помощью класса Lang
мы можем установить язык результата:
Result result = Results.html();
lang.setLanguage("fr", result);
Точно так же, используя класс Messages
, мы можем получить сообщение для конкретного языка:
Optional<String> language = Optional.of("fr");
String helloMsg = msg.get("helloMsg", language).get();
12. Настойчивость
Ninja поддерживает JPA 2.0 и использует Hibernate для обеспечения сохранения в веб-приложении. Кроме того, он предлагает встроенную поддержку базы данных H2 для быстрой разработки.
12.1. Модель
Нам требуется класс Entity
для подключения к таблице в базе данных. Для этого Ninja следует соглашению о поиске классов сущностей в пакете моделей .
Итак, создадим там класс сущности User :
@Entity
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Long id;
public String firstName;
public String email;
}
Затем мы настроим Hibernate и установим детали для подключения к базе данных.
12.2. Конфигурация
Для конфигурации Hibernate Ninja ожидает, что файл persistence.xml
будет находиться в каталоге src/main/java/META-INF
:
<?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/persistence_2_0.xsd"
version="2.0">
<!-- Database settings for development -->
<persistence-unit name="dev_unit"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.connection.driver_class" value="org.h2.Driver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.autocommit" value="true" />
</properties>
</persistence-unit>
</persistence>
Затем мы добавим детали подключения к базе данных в application.conf
:
ninja.jpa.persistence_unit_name=dev_unit
db.connection.url=jdbc:h2:./devDb
db.connection.username=sa
db.connection.password=
12.3. EntityManager
Наконец, мы внедрим экземпляр EntityManager
в ApplicationController
, используя класс Provider
Guice :
public class ApplicationController {
@Inject
Provider<EntityManager> entityManagerProvider;
// ...
}
Итак, мы готовы использовать EntityManager
для сохранения объекта User :
@Transactional
public Result insertUser(User user) {
EntityManager entityManager = entityManagerProvider.get();
entityManager.persist(user);
entityManager.flush();
return Results.redirect("/home");
}
Точно так же мы можем использовать EntityManager
для чтения объекта User
из БД:
@UnitOfWork
public Result fetchUsers() {
EntityManager entityManager = entityManagerProvider.get();
Query q = entityManager.createQuery("SELECT x FROM User x");
List<User> users = (List<User>) q.getResultList();
return Results.json().render(users);
}
Здесь аннотация Ninja @UnitOfWork
будет обрабатывать все, что касается соединений с базой данных, не имея дело с транзакциями. Следовательно, он может оказаться удобным для запросов только для чтения, где нам обычно не требуются транзакции.
13. Проверка
Ninja обеспечивает встроенную поддержку проверки bean-компонентов в соответствии со спецификациями JSR303 .
Давайте рассмотрим эту функцию, аннотировав свойство в сущности User аннотацией
@NotNull
:
public class User {
// ...
@NotNull
public String firstName;
}
Затем мы изменим уже обсуждавшийся метод insertUser
в ApplicationController
, чтобы включить проверку:
@Transactional
public Result insertUser(FlashScope flashScope, @JSR303Validation User user, Validation validation) {
if (validation.getViolations().size() > 0) {
flashScope.error("Validation Error: User can't be created");
} else {
EntityManager entityManager = entitiyManagerProvider.get();
entityManager.persist(user);
entityManager.flush();
flashScope.success("User '" + user + "' is created successfully");
}
return Results.redirect("/home");
}
Мы использовали аннотацию Ninja @JSR303Validation
, чтобы включить проверку объекта User
. Затем мы добавили аргумент Validation
для работы с проверками с помощью таких методов, как hasViolations
, getViolations
и addViolation.
Наконец, объект FlashScope
используется для отображения ошибки проверки на экране.
Примечание. Ninja следует спецификациям JSR303 для проверки компонентов. Однако спецификация JSR380 ( Bean Validation 2.0 ) является новым стандартом.
14. Заключение
В этой статье мы рассмотрели веб-фреймворк Ninja — полнофункциональный фреймворк, предоставляющий удобные функции с использованием популярных библиотек Java.
Для начала мы создали простое веб-приложение с использованием контроллеров
, моделей
и сервисов
. Затем мы включили поддержку JPA в приложении для постоянства.
В то же время мы увидели несколько основных функций, таких как Routes, рендеринг JSON, интернационализация и Flash Scopes.
Наконец, мы изучили поддержку проверки, предоставляемую фреймворком.
Как обычно, все реализации кода доступны на GitHub .