Перейти к основному содержимому

Введение в ниндзя-фреймворк

· 9 мин. чтения

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 :

./a136181a5b338f27e54be044b99aa970.png

4. Структура проекта

Давайте взглянем на Maven-подобную структуру проекта, созданную Ninja:

./21dffe30399f8adedb8f1f025499a91d.png

Фреймворк создает несколько пакетов на основе соглашений.

**Классы 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 :

./c9056a1346883e4c9071e1c02b90cf4b.png

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 :

./9021af8d4ff6d765ded45a66222c9682.png

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 :

./e40a7a42cffdbf87ee865b3b6198d1b8.png

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 .