- PVSM.RU - https://www.pvsm.ru -
Довольно давно я опубликовал на Хабре статью [1], где рассказал про свой проект, TeaVM [2]. С тех пор много всего произошло с ним, в том числе одна важная вещь, про которую речь пойдёт ниже и ради которой я решил снова написать на Хабр. Но для начала кратко напомню, про что проект.
Итак, TeaVM — это компилятор байт-кода Java в JavaScript. Идея создания TeaVM пришла мне, пока я работал full-stack Java разработчиком и использовал для написания фронтэнда GWT. В те времена (а это где-то лет 5 назад) не были широко распространены инструменты вроде node.js, webpack, babel, TypeScript; Angular был в первой версии, а альтернатив вроде React и vue.js не было вообще. Тогда ещё на полном серьёзе люди тестировали сайты в IE7 (а некоторые, кому не повезло с заказчиками, даже IE6). В целом, экосистема JavaScript была гораздо менее зрелой, чем сейчас, и без боли писать на JavaScript было нельзя.
GWT мне нравился тем, что на фоне всего этого он казался адекватным решением, хотя и не лишённым своих недостатков. Основные проблемы перечислены под катом:
Мне не казалось, что сама по себе идея написания веб-приложений на Java — плоха. Все недостатки GWT были от того, что Google, как мне кажется, просто не вложили достаточных ресурсов в его развитие. Я предпочитал мириться с недостатками GWT, лишь бы не переходить на JavaScript.
И тогда я подумал — а почему бы не использовать в качестве входного материала байт-код Java? Байт-код сохраняет много информации об исходной программе, настолько много, что декомпиляторы умудряются потом почти точь-в-точь восстановить исходники. Javaс делает всю самую сложную работу по генерации байт-кода, поэтому на генерацию JavaScript должно потратиться совсем немного времени. Плюсом получаем поддержку других языков для JVM и почти бесплатную поддержку новых версий Java (байт-код гораздо консервативнее, чем язык Java).
Я уже говорил, что с проектом произошло много всего интересного. Вот самые важные моменты, о которых хотелось бы рассказать:
Thread.sleep()
), приостанавливается и позволяет выполниться другому треду.Веб-фреймворк для TeaVM называется Flavour и он целиком написан на Java. Недавно я опубликовал первую версию (за номером 0.1.0) на Maven Central. Идеологически он напоминает современные Angular 2/4 и Vue.js, но построен целиком на идиомах, близких Java-разработчику. Например, все компоненты Flavour представлены обычными Java-классами, размеченными аннотациями, нет никаких отдельных props и state, или каких-то специальных объектов, которые должны инкапсулировать изменяющиеся свойства. Язык HTML-шаблонов полностью статически типизирован, любое обращение к свойству объекта или вызов обработчика события проверяются во время компиляции и, например, опечатки в названии свойств просто не дадут скомпилировать проект.
Для коммуникации с сервером Flavour предлагает использовать интерфейсы, размеченные аннотациями JAX-RS, а данные передаются с помощью DTO, которые в свою очередь размечаются аннотациями Jackson. Это должно быть удобно разработчикам на Java, которые, весьма вероятно, уже знают и используют эти API в своих проектах.
Возникает закономерный вопрос: а зачем создавать фреймворк, если существуют имеющиеся: React, Angular, Vue.js? Можно же просто воспользоваться JavaScript интеропом и ничего не изобретать. Конечно же, я думал об этом. Но нет, всё оказывается намного хуже, чем кажется на первый взгляд. Эти фреймворки построены вокруг идиом динамически типизированного JavaScript, а в нём и объект с нужным набором свойств можно создать из воздуха, и понадеяться, что у "класса" объекта есть магический метод с нужным названием. Вообще, в мире JavaScript, создатели фреймворков не привыкли думать про типизацию. Преодолеть это можно написанием всяческих адаптеров, обёрток, препроцессоров, генераторов. Но в итоге получится система посложнее исходных фреймворков, поэтому было решено написать свой.
Конечно, все интересующиеся могут почитать документацию на сайте [2]. Но чтобы было проще и быстрее ощутить вкус Flavour, покажу небольшой пример здесь.
Итак, создать проект можно с помощью Maven:
mvn archetype:generate
-DarchetypeGroupId=org.teavm.flavour
-DarchetypeArtifactId=teavm-flavour-application
-DarchetypeVersion=0.1.0
Собирается сгенерированный проект, как и ожидается, командой mvn package
.
Страничка описывается двумя файлами — кодом класса, который описывает её поведение (предоставляет данные для отображения, содержит обработчики событий) и HTML-шаблоном. В созданном проекте уже имеется пример странички, но я всё-таки приведу ещё один пример. Вы можете заменить сгенерированный из архетипа пример или просто добавить ещё два файла к имеющимся. Вот так выглядит код класса странички:
@BindTemplate("templates/fibonacci.html")
public class Fibonacci {
private List<Integer> values = new ArrayList<>();
public Fibonacci() {
values.add(0);
values.add(1);
}
public List<Integer> getValues() {
return values;
}
public void next() {
values.add(values.get(values.size() - 2) + values.get(values.size() - 1));
}
}
А вот так — её шаблон:
<ul>
<!-- values - это сокращение для this.getValues() -->
<std:foreach var="fib" in="values">
<li>
<html:text value="fib"/>
</li>
</std:foreach>
<li>
<!-- на самом деле, event:click может просто выполнить какой-то кусочек кода,
но для удобства рекомендуется обработчик события умещать в метод,
а из шаблона просто вызывать его -->
<button type="button" event:click="next()">Show next</button>
</li>
</ul>
std:foreach
, html:text
и event:click
— это компоненты Flavour. Пользователь может описывать свои компоненты (интересующиеся, как именно, могут почитать об этом в документации [3]), при этом они могут либо вручную отрисовывать свой DOM, либо делать это через шаблон. В этих компонентах нет ничего особенного, они не реализуются компиляторной магией. При желании вы можете написать свои аналоги. Для иллюстрации можете ознакомиться с кодом html:text [4].
Наконец, вот как должен выглядеть код метода main
:
public static void main(String[] args) {
Templates.bind(new Fibonacci(), "application-content");
}
Вся основная магия стартует именно здесь. Фреймворк не создаёт экземпляр класса страницы, и никак не управляет им. Вместо этого вы создаёте его сами и сами же им управляете как хотите, а Flavour просто генерирует DOM, вставляет его в нужное место, отслеживает изменения состояния объекта и перерисовывает DOM согласно этим изменениям. Кстати, Flavour не перерисовывает весь DOM, а меняет только необходимую его часть.
Хочу ещё раз отметить, что шаблолны статически типизированы. Если ошибиться и написать event:click="nxt()"
, то компилятор напишет сообщение об ошибке. Такой подход позволяет ещё и генерировать более быстрый код — Flavour не тратит время после загрузки страницы, чтобы распарсить директивы и проинициализировать байндинги; он всё это делает во время компиляции.
Теперь мне хотелось бы показать, как Flavour может быть полезен fullstack-разработчику. Допустим, вы используете какой-нибудь CXF в связке с JAX-RS. Вы написали примерно такой интерфейс:
@Path("math")
public interface MathService {
@GET
@Path("integers/sum")
int sum(@QueryParam("a") int a, @QueryParam("b") int b);
}
и реализовали его (например, в классе MathServiceImpl
), зарегистрировали реализацию в CXF. У вас готов небольшой REST-сервис. Теперь, чтобы сделать на него запрос, со стороны клиента можно написать такой код:
MathService math = RESTClient.factory(MathService.class).createResource("api");
System.out.println(math.sum(2, 3));
(можно увидеть в devtools, что этот код пошлёт GET-запрос на адрес /api/math/integers/sum?a=2&b=3
).
В общем, не надо каким-то образом объяснять веб-клиенту, как правильно делать REST-запрос на нужный endpoint. Вы уже это сделали единым образом для сервера и для клиента. Можно дальше наращивать и рефакторить REST-сервис, при этом не надо синхронизировать это со стороны сервера и со стороны клиента — есть точка синхронизации в виде интерфейса MathService
.
В GWT есть аналогичный механизм, GWT-RPC, но он заставляет генерировать дополнительный async-интерфейс и использовать колбэки, тогда как TeaVM умеет преобразовывать синхронный код в асинхронный. И GWT-RPC использует свой, ни с чем не совместимый протокол, так что создав endpoint для GWT-RPC, вы не сможете его переиспользовать, например, для iOS-клиента.
Разумеется, в небольшой обзорной статье я не могу рассказать обо всём вообще. Поэтому просто упомяну, что такого интересного есть в TeaVM и Flavour, что делает их вполне пригодными для создания качественных веб-приложений.
В последнее время экосистема разработки JavaScript стала вполне зрелой. Разработчику проще не использовать всяких тяжеловесных монстров вроде GWT, а научиться настраивать инструменты, ставшие стандартами де-факто, и писать на современном языке с большим количеством продвинутых фич или даже на современном статически типизированном языке (TypeScript).
Проблема в том, что всё это надо изучать. Изучать не просто синтаксис языка (на это опытный разработчик потратит несколько дней), изучать много всего — библиотеки, идиомы, инструменты разработчика. Уверен, что вчерашний опытный Java-разработчик возьмёт и разберётся со всем этим за пару недель, но вопрос в том, насколько хорошо он успеет разобраться? Сможет ли он писать действительно хороший код? Даже если разберётся и сможет, есть ещё проблемы. Во-первых, разработчику придётся переключать контекст между Java и JavaScript. Во-вторых, так разработчику придётся потратить больше времени на настройку инструментов.
Ну и у меня есть вопрос к Java-коммьюнити. Почему-то же есть со стороны JavaScript такое настойчивое движение в сторону бэкэнда? Подозреваю, что ровно из тех соображений, которые я привёл выше. Почему бы не быть такому же движению в обратном направлении? Вот сообщество JavaScript frontend разработчиков скооперировалось и породило backend экосистему вокруг node.js. Чем мы, сообщество разработчиков на Java, хуже? Есть такой миф, что якобы JavaScript шустрый и легковесный, а Java — большая и тяжеловесная, и не подходит для создания фронтэнда. На самом деле, своим проектом я пытаюсь доказать, что это именно что миф, и что правильно приготовленная Java тоже может быть маленькой и шустрой. Тому пример — реализация TodoMVC, которая занимает 125kb (попробуйте написать TodoMVC на React или Angular 4 и посмотрите, какой здоровенный у вас получится бандл).
Если вы заинтересовались Flavour, вот вам ещё немного материала для изучения:
Мне очень хотелось бы получить обратную связь. Интересен ли вам мой проект, хотели бы вы его попробовать для написания небольшого приложения? Нужно ли мне ещё публиковать статьи, и если да, о чём именно вы хотели бы в нём прочитать? Я могу публиковать туториалы по Flavour, могу рассказывать, как он устроен внутри, как работает TeaVM. Что из этого вам интереснее?
Автор: konsoletyper
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/265567
Ссылки в тексте:
[1] статью: https://habrahabr.ru/post/240999/
[2] TeaVM: http://teavm.org/
[3] документации: http://teavm.org/docs/flavour/custom-components.html
[4] html:text: https://github.com/konsoletyper/teavm-flavour/blob/master/templates/src/main/java/org/teavm/flavour/components/html/TextComponent.java
[5] Документация: http://teavm.org/docs/intro/overview.html
[6] TodoMVC: https://github.com/konsoletyper/teavm-flavour-examples-todomvc
[7] Сапёр: https://github.com/konsoletyper/teavm-flavour-minesweeper
[8] Пример CRUD, близкий к реальному: https://github.com/konsoletyper/teavm-flavour/tree/master/example
[9] Источник: https://habrahabr.ru/post/339998/
Нажмите здесь для печати.