Введение в Hilla: полнофункциональная Java-инфраструктура

автор vadim


Hilla сочетает в себе серверную часть Java, созданную на Spring, с внешней частью TypeScript, созданной с помощью Lit, быстрой реактивной среды для JavaScript. Hilla, основанная на Vaadin Fusion, представляет собой уникальное животное в экосистеме Java: что-то вроде Next.js для JavaScript, но с серверной частью Java на основе Spring. В этой статье вы познакомитесь с Hilla, в том числе о том, как создать базовое веб-приложение, создать внешний интерфейс и добавить новые компоненты.

Хилла и Ваадин

В январе этого года разработчики Vaadin объявили, что переименовывают Vaadin Fusion в Hilla. Для разработчиков, уже знакомых с Fusion, изменилось только название. Разработчики, только открывающие для себя Hilla, заметят, что в примерах в этой статье используются пакеты и компоненты, названные в честь Vaadin. В будущем выпуске пакеты Vaadin Fusion будут переименованы в Hilla.

Веб-разработка на Java с реактивным интерфейсом

Hilla объединяет реактивный интерфейс JavaScript и серверную часть Spring Java в одной унифицированной сборке. Примеры в этой статье покажут вам, как эти компоненты работают вместе, образуя Hilla полнофункциональную среду. Чтобы продолжить, вам понадобятся оба Node.js (npm) и последний JDK, установленный в вашей системе. Убедитесь, что оба node -v и java –version работа!

Чтобы начать, откройте командную строку и создайте новый проект с помощью npxкак показано в листинге 1.

Листинг 1. Строительные леса для нового проекта в Хилле

npx @vaadin/cli init --hilla foundry-hilla

Сейчас, cd в новый каталог и введите ./mvnw (или mvnw для Windows). Эта команда инициирует сборку Maven. Вы увидите журнал вывода как для внутренней, так и для создаваемой внешней части. Вскоре приложение будет запущено в режиме разработки.

образец холмаИДГ

Рисунок 1. Посетите localhost:8080, и вы увидите, что ваше приложение Hilla работает.

Если вы посмотрите на только что созданную файловую систему, вы увидите, что ее структура разделена на стандартную структуру Maven и внешний каталог:

  • /project-root
    • /frontend
      • html
      • ts
      • ts
      • /stores
      • /themes
      • /views
    • /src
    • /target

Корень проекта содержит файл сборки Maven (pom.xml), который создает код Java из /src into /targetи вызывает инструмент сборки JavaScript (vite.js) для создания клиентского приложения, содержащегося в /frontend.

Создайте переднюю часть

В Hilla интерфейс загружается из /front-end/index.html, /front-end/index.tsи routes.ts файлы. Вместе эти файлы определяют маршрутизацию и устанавливают содержимое страницы для данного маршрута. Самая поучительная из этих страниц — routes.tsпоказанный в листинге 2.

Листинг 2. Routes.ts

import { Route } from '@vaadin/router';
import './views/helloworld/hello-world-view';
import './views/main-layout';
export type ViewRoute = Route & {
 title?: string;
 icon?: string;
 children?: ViewRoute[];
};
export const views: ViewRoute[] = [
 // place routes below (more info https://vaadin.com/docs/latest/fusion/routing/overview)
 {
   path: '',
   component: 'hello-world-view',
   icon: '',
   title: '',
 },
 {
   path: 'hello',
   component: 'hello-world-view',
   icon: 'la la-globe',
   title: 'Hello World',
 },
 {
   path: 'about',
   component: 'about-view',
   icon: 'la la-file',
   title: 'About',
   action: async (_context, _command) => {
     await import('./views/about/about-view');
     return;
   },
 },
];
export const routes: ViewRoute[] = [
 {
   path: '',
   component: 'main-layout',
   children: [...views],
 },
];

Код в листинге 2 связывает путь с компонентом. Как и многие фреймворки JavaScript, Hilla использует компонент для представления представления. В этом случае, когда пользователь переходит по пустому маршруту, он будет обслуживать hello-world-view компонент. (Обратите внимание, что другие маршруты предоставляют дополнительную информацию, такую ​​как значок, заголовок и действие.)

Основной макет обрабатывается /frontend/views/main-layout.tsв то время как содержание hello-world-view находится в /frontend/views/helloworld/hello-world-view.tsпоказанный в листинге 3.

Листинг 3. hello-world-view.ts

import '@vaadin/button';
import '@vaadin/notification';
import { Notification } from '@vaadin/notification';
import '@vaadin/text-field';
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { View } from '../../views/view';
@customElement('hello-world-view')
export class HelloWorldView extends View {
 name="";
 connectedCallback() {
   super.connectedCallback();
   this.classList.add('flex', 'p-m', 'gap-m', 'items-end');
 }
 render() {
   return html`
     <vaadin-text-field label="Your name" @value-changed=${this.nameChanged}></vaadin-text-field>
     <vaadin-button @click=${this.sayHello}>Say hello</vaadin-button>
   `;
 }
 nameChanged(e: CustomEvent) {
   this.name = e.detail.value;
 }
 sayHello() {
   Notification.show(`Hello ${this.name}`);
 }
}

Код в листинге 3 показывает, как Лит строит представление. Если вы знакомы с идиомами реактивного JavaScript, источник должен быть достаточно ясен. Если нет, см. мое недавнее введение в лит. render() Метод отвечает за вывод содержимого представления. Мы будем использовать его здесь как место для изучения вещей. В частности, мы хотим увидеть, как соединить этот интерфейс с нашими внутренними конечными точками Java.

Создайте конечные точки Java

Hilla построена на основе Spring Boot, поэтому мы можем создавать конечные точки с помощью Spring Web, как обычно. Hilla предлагает дополнительные функции для автоматического создания TypeScript, который будет использоваться во внешнем интерфейсе Lit.

Начните с создания нового файла в /src/java/main/com/example/application называется MyEndpoint.java. Вставьте содержимое листинга 4 в этот файл.

Листинг 4. MyEndpoint.java

package com.example.application;

import com.vaadin.flow.server.auth.AnonymousAllowed;
import dev.hilla.Endpoint;
import dev.hilla.Nonnull;

@Endpoint
@AnonymousAllowed
Public @Nonnull class MyEndpoint {
   public String foo() {
       return "bar";
   }
}

Хилла @Endpoint аннотация сообщает платформе, что этот класс является REST API. Класс также помечен с помощью @AnonymousAllowed аннотация, потому что Hilla по умолчанию защищает все конечные точки с помощью безопасности Spring. @Nonnull аннотация генерирует правильную привязку типа для внешнего интерфейса TypeScript.

После сохранения этого файла класса вы можете заметить, что Hilla сгенерировала новый файл TypeScript в /frontend/generated/MyEndpoint.ts. Мы будем использовать этот модуль для достижения конечной точки из представления.

Примечание: Не вносите изменения в эти сгенерированные файлы; Hilla перезапишет их в зависимости от изменений в файле Java.

Теперь вернитесь к frontend/views/helloworld/hello-world-view.ts, где мы заставим работать нашу простую конечную точку. В этом случае мы хотим вывести содержимое вызова foo() конечная точка (которая представляет собой «bar»). В листинге 5 показаны дополнения, которые следует внести в hello-world-view.ts файл. (Обратите внимание, что я удалил большую часть предыдущего кода и оставил только дополнения для этого листинга.)

Листинг 5. Hello-world-view.ts

//...
import { customElement,property } from 'lit/decorators.js';
import { foo } from 'Frontend/generated/MyEndpoint';

@customElement('hello-world-view')
export class HelloWorldView extends View {
  //...
  @property()
  myString: string = "";
  constructor() {
    super();
    this.getString();
  }
  render() {
     return html`
     //...
     <div>${this.myString}</div>
   `;
 }

  async getString() {
     this.myString = await foo();
   }
}

Главное здесь — импортировать foo() функция от MyEndpoint модуль, а затем используйте его для вызова удаленного внутреннего метода Java, который мы определили ранее.

Для этого мы определяем реактивное свойство класса с помощью аннотации Lit TypeScript. @propertyс именем string. Мы будем использовать это свойство для хранения значения с сервера. Чтобы заполнить его, мы вызываем async getString() метод, который просто вызывает foo() функцию и помещает возвращаемое значение в myString.

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

Использование компонентов Vaadin в Hilla

Как я уже отмечал ранее, Hilla — это Vaadin Fusion, поэтому приложения, созданные с помощью Hilla, могут использовать преимущества всех хорошо продуманных компонентов, которые вы, возможно, знаете из этой среды. В качестве примера давайте воспользуемся компонентом сетки Vaadin для загрузки коллекции романов с их названиями и авторами.

Сначала мы создадим объект модели, который просто содержит два Strings, как показано в листинге 6. Этот файл представляет собой типичный объект данных Java. Сохраните это как /src/main/java/com/example/application/Novel.java.

Листинг 6. Объект модели для хранения романов

package com.example.application;
import javax.validation.constraints.NotBlank;
public class Novel {
   @NotBlank
   private String title;
   @NotBlank
   private String author;
   public Novel(String title, String author){
     this.title = title;
     this.author = author;
   }
   public String getTitle() {
       return title;
   }
   public void setTitle(String title) {
       this.title = title;
   }
   public String getAuthor() {
       return author;
   }
   public void setAuthor(String author) {
       this.author = author;
   }
}

В листинге 7 мы обслуживаем List романов из MyEndpoint.

Листинг 7. MyEndpoint со списком моих любимых романов

package com.example.application;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import dev.hilla.Endpoint;
import dev.hilla.Nonnull;
@Endpoint
@AnonymousAllowed
public class MyEndpoint {
   private final List<Novel> novels = new ArrayList<Novel>();
   MyEndpoint(){
     Novel empireFalls = new Novel("Empire Falls", "Richard Russo");
     Novel reservationBlues = new Novel("Reservation Blues", "Sherman Alexie");
     Novel theAthenianMurders = new Novel("The Athenian Murders", "José Carlos Somoza");
     this.novels.add(empireFalls);
     this.novels.add(reservationBlues);
     this.novels.add(theAthenianMurders);
   }

   public @Nonnull List<Novel> getNovels() {
     return this.novels;
   }
}

В листинге 7 мы подготовили несколько романов с их авторами и вставили их в novels свойство. Затем мы представили данные в getNovels() конечная точка.

Теперь давайте отобразим новые данные, как показано в листинге 8. (Обратите внимание, что в листинге 8 показаны только измененные части кода.)

Листинг 8. Использование сетки для отображения романов

//...
import { foo, getNovels } from 'Frontend/generated/MyEndpoint';
import '@vaadin/grid/vaadin-grid';
@customElement('hello-world-view')
export class HelloWorldView extends View {
 @property()
 novels: object = {};
 constructor() {
   //...
   this.initNovels();
 }
 render() {
   return html`
     <vaadin-grid .items="${this.novels}" theme="row-stripes">
       <vaadin-grid-column path="title"></vaadin-grid-column>
       <vaadin-grid-column path="author"></vaadin-grid-column>
     </vaadin-grid>
   `;
 }
  async initNovels(){
     this.novels = await getNovels();
}

В этом листинге мы импортируем getNovels объект из frontend/generated/MyEndpont, который Хилла сгенерировала для нас. Затем мы используем этот метод в качестве источника содержимого this.novels.

Далее мы используем this.novels предоставить .items собственность ввозимому vaadin-grid компонент. Конечным результатом является красиво отформатированный компонент сетки с минимальными усилиями.

Заключение

В этой статье представлена ​​Hilla, полнофункциональная платформа, основанная на Vaadin Fusion. Hilla предлагает хорошо интегрированный опыт создания веб-приложений Java с реактивным интерфейсом. Благодаря Vaadin в нем имеется множество готовых к использованию полезных компонентов. Примеры в этой статье должны дать вам представление о работе с Hilla.

Related Posts

Оставить комментарий