Въведение в Ninja Framework

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 можем да добавим най-новата зависимост от ядрото на нинджа към pom.xml :

 org.ninjaframework ninja-core 6.5.0 

След това ще стартираме командата Maven за компилиране на файловете за първи път:

mvn clean install

И накрая, нека стартираме приложението с помощта на командата Maven, предоставена от Ninja:

mvn ninja:run

Voila! Нашето приложение е стартирано и ще бъде достъпно на localhost: 8080 :

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

Нека да разгледаме структурата на проекта, подобна на Maven, създадена от Ninja:

Рамката създава няколко пакета въз основа на конвенции.

Класовете Java са категоризирани в директории conf , контролери , модели и услуги в 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(); } }

Тук методът на индекса ще изобрази HTML, като извика метода html от класа Резултати . Обектът Result съдържа всичко необходимо за изобразяване на съдържанието като код за отговор, заглавки и бисквитки.

Забележка: Анотацията на Guice @Singleton позволява само един екземпляр на контролера в цялото приложение .

6. Изглед

За метода на индекса Ninja ще потърси HTML файла - index .ftl.html под директорията views / ApplicationController .

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

Нека създадем файла i ndex .ftl.html за метода на индекса :

 Ninja: Index User Json 

Тук използвахме предоставения от Ninja таг i18n , за да получим свойството helloMsg от файла message.properties . По-нататък ще обсъдим това в раздела за интернационализация.

7. Маршрут

След това ще определим маршрута на заявката за достигане до метода на индекса .

Ninja използва класа Routes в пакета conf, за да присвои URL към определен метод на контролера.

Нека добавим маршрут за достъп до метода на индекса на 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 от класа Резултати .

Нека добавим метода userJson в класа ApplicationController и да изобразим съдържанието на проста HashMap в JSON:

public Result userJson() { HashMap 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 getUserMap(); }

След това ще внедрим интерфейса UserService в класа UserServiceImpl и ще заменим метода getUserMap :

public class UserServiceImpl implements UserService { @Override public HashMap getUserMap() { HashMap userMap = new HashMap(); userMap.put("name", "Norman Lewis"); userMap.put("email", "[email protected]"); return userMap; } }

Then, we'll bind the UserService interface with the UserServiceImpl class using Ninja's dependency injection feature provided by Guice.

Let's add the binding in the Module class available in the conf package:

@Singleton public class Module extends AbstractModule { protected void configure() { bind(UserService.class).to(UserServiceImpl.class); } }

Last, we'll inject the UserService dependency in the ApplicationController class using the @Inject annotation:

public class ApplicationController { @Inject UserService userService; // ... }

Thus, we're all set to use the UserService‘s getUserMap method in the ApplicationController:

public Result userJson() { HashMap userMap = userService.getUserMap(); return Results.json().render(userMap); }

10. Flash Scope

Ninja provides a simple yet efficient way to handle success and error messages from requests through its feature called Flash Scope.

To use it in the controller, we'll add the FlashScope argument to the method:

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

Note: The redirect method of the Results class redirects the target to the provided URL.

Then, we'll add a routing /flash to the showFlashMsg method and modify the view to show the flash messages:

 ${flash.error} ${flash.success} 

Now, we can see the FlashScope in action at localhost:8080/flash:

11. Internationalization

Ninja provides a built-in internationalization feature that is easy to configure.

First, we'll define the list of supported languages in the application.conf file:

application.languages=fr,en

Then, we'll create the default properties file – messages.properties for English – with key-value pairs for messages:

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

Similarly, we can add the language code in the file name for a language-specific properties file — for instance, message_fr.properties file for French:

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

Once the configurations are ready, we can easily enable internationalization in the ApplicationController class.

We've got two ways, either by using the Lang class or the Messages class:

@Singleton public class ApplicationController { @Inject Lang lang; @Inject Messages msg; // ... }

Then, using the Lang class, we can set the language of the result:

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

Similarly, using the Messages class, we can get a language-specific message:

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

12. Persistence

Ninja supports JPA 2.0 and utilizes Hibernate to enable persistence in the web application. Also, it offers built-in H2 database support for rapid development.

12.1. Model

We require an Entity class to connect with a table in the database. For this, Ninja follows the convention of looking for the entity classes in the models package. So, we'll create the User entity class there:

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

Then, we'll configure Hibernate and set the details for the database connection.

12.2. Configuration

For Hibernate configuration, Ninja expects the persistence.xml file to be in the src/main/java/META-INF directory:

    org.hibernate.jpa.HibernatePersistenceProvider          

Then, we'll add the database connection details to 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

Last, we'll inject the instance of the EntityManager in the ApplicationController using Guice's Provider class:

public class ApplicationController { @Inject Provider entityManagerProvider; // ... }

So, we're ready to use the EntityManager to persist the User object:

@Transactional public Result insertUser(User user) { EntityManager entityManager = entityManagerProvider.get(); entityManager.persist(user); entityManager.flush(); return Results.redirect("/home"); }

Similarly, we can use the EntityManager to read the User object from the DB:

@UnitOfWork public Result fetchUsers() { EntityManager entityManager = entityManagerProvider.get(); Query q = entityManager.createQuery("SELECT x FROM User x"); List users = (List) q.getResultList(); return Results.json().render(users); }

Here, Ninja's @UnitOfWork annotation will handle everything about the database connections without dealing with transactions. Hence, it can prove handy for read-only queries, where we usually don't require transactions.

13. Validation

Ninja provides built-in support for bean validations by following the JSR303 specifications.

Let's examine the feature by annotating a property in the User entity with the @NotNull annotation:

public class User { // ... @NotNull public String firstName; }

Then, we'll modify the already discussed insertUser method in the ApplicationController to enable the validation:

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

We've used Ninja's @JSR303Validation annotation to enable the validation of the User object. Then, we've added the Validation argument to work with validations through methods like hasViolations, getViolations, and addViolation.

Last, the FlashScope object is used to show the validation error on the screen.

Note: Ninja follows the JSR303 specifications for bean validations. However, the JSR380 specification (Bean Validation 2.0) is the new standard.

14. Conclusion

In this article, we explored the Ninja web framework — a full-stack framework that provides handy features using popular Java libraries.

To begin with, we created a simple web application using controllers, models, and services. Then, we enabled JPA support in the app for persistence.

At the same time, we saw a few basic features like Routes, JSON rendering, Internationalization, and Flash Scopes.

Last, we explored the validation support provided by the framework.

As usual, all the code implementations are available over on GitHub.