«1. Обзор

В настоящее время существует множество сред на основе JEE, таких как Spring, Play и Grails, доступных для разработки веб-приложений.

У нас могут быть причины предпочесть один из них другим. Однако наш выбор также зависит от варианта использования и проблемы, которую мы пытаемся решить.

В этом вводном руководстве мы изучим веб-фреймворк Ninja и создадим простое веб-приложение. В то же время мы рассмотрим несколько основных функций, которые он предоставляет.

2. Ninja

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 содержит соответствующие классы модульных тестов.

Каталог views в 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. View

Для метода index Ninja будет искать файл HTML — index.ftl.html в каталоге views/ApplicationController.

Ninja использует механизм шаблонов Freemarker для рендеринга HTML. Таким образом, все файлы в представлениях должны иметь расширение .ftl.html.

Давайте создадим файл index.ftl.html для метода index:

<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", "[email protected]");    
    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", "[email protected]"); 
        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. Flash Scope

Ninja предоставляет простой, но эффективный способ обработки сообщений об успешном выполнении и ошибках из запросов с помощью своей функции, называемой Объем вспышки.

Чтобы использовать его в контроллере, мы добавим в метод аргумент FlashScope:

public Result showFlashMsg(FlashScope flashScope) {
    flashScope.success("Success message");
    flashScope.error("Error message");
    return Results.redirect("/home");
}

Примечание. Метод перенаправления класса Results перенаправляет цель на предоставленный URL-адрес.

Затем мы добавим маршрутизацию /flash в метод showFlashMsg и изменим представление, чтобы отображались сообщения flash:


<#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 в действии на локальном хосте: 8080/flash: ~ ~~ 11. Интернационализация

Ninja предоставляет встроенную функцию интернационализации, которую легко настроить.

Сначала мы определим список поддерживаемых языков в файле application.conf:

Затем мы создадим файл свойств по умолчанию — messages.properties для английского языка — с ключом пары -value для сообщений:

application.languages=fr,en

Точно так же мы можем добавить код языка в имя файла для файла свойств для конкретного языка — например, файл message_fr.properties для французского языка:

header.home=Home!
helloMsg=Hello, welcome to Ninja Framework!

Когда конфигурации готовы, мы можем легко включить интернационализацию в классе ApplicationController.

header.home=Accueil!
helloMsg=Bonjour, bienvenue dans Ninja Framework!

У нас есть два способа: использовать класс Lang или класс Messages:

Затем, используя класс Lang, мы можем установить язык результата:

@Singleton
public class ApplicationController {
    @Inject
    Lang lang;

    @Inject
    Messages msg;
    
    // ...
}

Аналогично , используя класс Messages, мы можем получить сообщение для конкретного языка:

Result result = Results.html();
lang.setLanguage("fr", result);

12. Постоянство

Optional<String> language = Optional.of("fr");        
String helloMsg = msg.get("helloMsg", language).get();

Ninja поддерживает JPA 2.0 и использует Hibernate для обеспечения сохранения в веб-приложении. Кроме того, он предлагает встроенную поддержку базы данных H2 для быстрой разработки.

12.1. Модель

Нам требуется класс Entity для соединения с таблицей в базе данных. Для этого Ninja следует соглашению о поиске классов сущностей в пакете моделей. Итак, мы создадим там класс сущности User:

Затем мы настроим Hibernate и установим детали для подключения к базе данных.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id;
    public String firstName;
    public String email;  
}

12.2. Конфигурация

Для конфигурации Hibernate Ninja ожидает, что файл persistence.xml будет находиться в каталоге src/main/java/META-INF:

Затем мы добавим детали подключения к базе данных в application.conf :

<?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>

12.3. EntityManager

ninja.jpa.persistence_unit_name=dev_unit
db.connection.url=jdbc:h2:./devDb
db.connection.username=sa
db.connection.password=

Наконец, мы добавим экземпляр EntityManager в ApplicationController, используя класс Provider Guice:

Итак, мы готовы использовать EntityManager для сохранения объекта User:

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");
}

«

@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.