Helidon: простой и быстрый Java фреймворк
Современная разработка веб приложений зачастую основывается на использовании фреймворка Spring. Spring имеет большое количество модулей и готовых удобных решений, но время идет, и мир JVM меняется. Наиболее известными решениями для web-приложений являются следующие фреймворки:
Эти фреймворки являются свежим взглядом на разработку серверных приложений на Java. Они поддерживают разработку приложений с использованием подхода Cloud Native.
В этой статье я хочу рассмотреть фреймворк Helidon. Мне он понравился своей простотой и скоростью инициализации приложения. Сам фреймворк имеет 2 модификации:
Helidon SE
Helidon SE — это легковесное решение, где по сути вам предоставляется только HTTP сервер, который вы можете сконфигурировать самостоятельно. Он не включает в себя модули IOC, методы по работе с базами данных и прочее.
А так как внутри себя Helidon использует высокоэффективный сетевой фреймворк Netty, это позволяет ему достаточно быстро инициализироваться и предоставить функционал для минимального приложения с быстрым стартом.
Давайте попробуем написать самое простое приложение, которое будет отдавать текущее время при помощи Helidon SE. Система сборки у нас будет Gradle с конфигурацией Kotlin DSL. Также нам понадобится IDE — Intellij IDEA и JDK 16.
Давайте инициализируем проект. Для этого нам необходимо создать директорию с проектом, затем перейти в терминале в нее.
Далее выполнить команду для инициализации проекта при помощи Gradle
gradle init --type java-application --test-framework junit-jupiter --dsl kotlin
Ответив на уточняющие вопросы о названии проекта и корневом пакете, выполните команду ./gradlew build
. В терминале должно вывестись сообщение об успешной сборке.
Теперь давайте откроем проект в IDE, и начнем подключать Helidon. Пока мы имеем пустой проект со следующей структурой:
.
├── app
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── dev
│ │ │ └── rmuhamedgaliev
│ │ │ └── App.java
│ │ └── resources
│ └── test
│ ├── java
│ │ └── dev
│ │ └── rmuhamedgaliev
│ │ └── AppTest.java
│ └── resources
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
Gradle нам создал базовую структуру приложения с главным классом App.java
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package dev.rmuhamedgaliev;
public class App {
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) {
System.out.println(new App().getGreeting());
}
}
Теперь давайте в зависимости добавим Helidon.
Для этого нам необходимо, согласно инструкции, добавить зависимости в app/build.gradle.kts и привести к следующему виду:
plugins {
application
id("org.kordamp.gradle.jandex") version "0.11.0"
}
repositories {
mavenCentral()
}
val helidonVersion = "2.3.2"
dependencies {
implementation(platform("io.helidon:helidon-dependencies:${helidonVersion}"))
implementation("io.helidon.microprofile.bundles:helidon-microprofile")
implementation("io.helidon.media:helidon-media-jackson")
runtimeOnly("org.jboss:jandex")
runtimeOnly("com.sun.activation:javax.activation:1.2.0")
testCompileOnly("org.junit.jupiter:junit-jupiter-api:5.7.2")
testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
}
application {
// Define the main class for the application.
mainClass.set("dev.rmuhamedgaliev.App")
}
tasks.test {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
Далее нам необходимо создать наш сервер и сконфигурировать его. Для этого давайте заменим содержимое файла App.java на следующее:
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package dev.rmuhamedgaliev;
import io.helidon.config.Config;
import io.helidon.health.HealthSupport;
import io.helidon.health.checks.HealthChecks;
import io.helidon.media.jackson.JacksonSupport;
import io.helidon.metrics.MetricsSupport;
import io.helidon.webserver.Routing;
import io.helidon.webserver.WebServer;
public class App {
public static void main(String[] args) {
// Инициализируем конфигурацию из файла resources/application.yaml
Config config = Config.create();
// Создаем экземпляр сервера и передаем конфигурацию
WebServer server = WebServer.builder(createRouting())
.config(config.get("server"))
.addMediaSupport(JacksonSupport.create())
.build();
// Запускаем сервер и выводим информацию о адресе, где запущен сервер
server.start().thenAccept(ws -> {
System.out.println("WEB server is up! http://localhost:" + ws.port() + "/time");
ws.whenShutdown().thenRun(() -> System.out.println("WEB server is DOWN. Good bye!"));
})
.exceptionallyAccept(t -> {
System.err.println("Startup failed: " + t.getMessage());
t.printStackTrace(System.err);
});
}
// Делаем роутинг для обращения
private static Routing createRouting() {
// Добавляем сервис который дает информацию о системе - память и прочее
HealthSupport health = HealthSupport.builder()
.addLiveness(HealthChecks.healthChecks())
.build();
// Включаем мониторинг метрик нашего сервера
MetricsSupport metrics = MetricsSupport.create();
// Берем инстанс нашего сервиса предоставляющего время
TimeService timeService = new TimeService();
return Routing.builder()
.register(health)
.register(metrics)
.register("/time", timeService)
.build();
}
}
Затем нам необходимо в директории app/src/main/resources/application.yaml указать порт и адрес сервера:
server:
port: 8080
host: 0.0.0.0
Теперь давайте создадим наш TimeService который будет давать информацию о времени:
package dev.rmuhamedgaliev;
import io.helidon.webserver.Routing;
import io.helidon.webserver.ServerRequest;
import io.helidon.webserver.ServerResponse;
import io.helidon.webserver.Service;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class TimeService implements Service {
/**
* Инициализируем методы сервиса, и указываем пути для обращения
* @param rules
*/
@Override
public void update(Routing.Rules rules) {
rules
.get("/", this::getUTCTime)
.get("/{timezone}", this::getTimeInTimezone);
}
private void getUTCTime(ServerRequest request, ServerResponse response) {
response.send(getTimeInZone("UTC"));
}
private void getTimeInTimezone(ServerRequest request, ServerResponse response) {
String timezone = request.path().param("timezone").replace("-", "/");
response.send(getTimeInZone(timezone));
}
private String getTimeInZone(String timeZone) {
return LocalDateTime.now(ZoneId.of(timeZone)).format(DateTimeFormatter.ISO_DATE_TIME);
}
}
И все, теперь у нас должно получиться рабочее приложение, давайте запустим его:
./gradlew :app:run
В терминале вы должны увидеть примерно такой ответ:
╰─$ ./gradlew :app:run
> Task :app:run
Execution optimizations have been disabled for task ':app:run' to ensure correctness due to the following reasons:
- Gradle detected a problem with the following location: '/Users/rinatmuhamedgaliev/Projects/MKDev/article/helidon-se-demo/app/build/resources/main'. Reason: Task ':app:run' uses this output of task ':app:jandex' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
Sept 05, 2021 3:14:29 PM io.helidon.common.HelidonFeatures features
INFO: Helidon SE 2.3.2 features: [Config, Health, Metrics, WebServer]
Sept 05, 2021 3:14:29 PM io.helidon.webserver.NettyWebServer lambda$start$7
INFO: Channel '@default' started: [id: 0x21bc78b3, L:/[0:0:0:0:0:0:0:0]:8080]
WEB server is up! http://localhost:8080/time
<==========---> 80% EXECUTING [35s]
> :app:run
Теперь в браузере можете открыть следующие адреса:
- http://localhost:8080/time - текущее время в UTC
- http://localhost:8080/time/Europe-Paris - текущее время в таймзоне Europe/Paris только вместо / мы используем -
- http://localhost:8080/health - данные о сервисе health
- http://localhost:8080/metrics - метрики работы сервиса metrics
Таким образом, мы получили базовый HTTP сервер с удобным созданием обработчиков и быстрым запуском.
Сам по себе Helidon умеет много, это вводная статья и тут показан самый простой пример. Если вам интересна тема, записывайтесь на консультацию, помогу вам погрузится в мир Java разработки!
Автор статьи предоставляет консультации и занимается индивидуальным обучением программированию на Java Нанять