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