Полагаю, что после известных событий специалисты, релоцировавшиеся за пределы РФ уже столкнулись с интересным явлением - за пределами нашей страны проекты на Java EE (Jakarta EE) заметно более распространены. Этому есть причины, но останавливаться на них я не буду, желающих получить больше подробностей отсылаю к оригинальной статье. Она не очень новая, 2021 года, но в целом правильно описывает сложившуюся картину, с той поправкой, что к настоящему моменту интеграция Jakarta EE в инфраструктуру Spring еще более продвинулась, см. например, статью, где хорошо представлена интеграция Spring Boot 3, Spring 6 и Jakarta 9 и 10.
Существует достаточно обширный диапазон корпоративных решений, в которых Jakarta EE имеет некоторые преимущества, например, в производительности. В РФ такие решения менее распространены (если не считать некоторого количества legacy решений на устаревших версиях Java EE, которые поддерживаются по необходимости, просто потому, что они когда-то были реализованы и не могут быть быстро модернизированы или заменены). Во всяком случае, не будем уподобляться тем двум junior-разработчиками, которые однажды устроили холивар на тему "каков процент приложений в которых используется Java EE" в моем присутствии. Мне тогда лень было вмешиваться в их разговор, но правильный ответ на вопрос в их споре - этот процент составляет 100%, просто лишь потому, что библиотеки Java EE/Jakarta EE давно уже интегрированы в ЯП Java.
Интеграция в Spring также достаточно глубока, настолько, что можно пользоваться многими технологиями, возникшими именно в рамках Java EE в приложениях на Spring Boot. Пример такой интеграции и будет показан в этой статье. Он будет полезен, например, в тех случаях, когда команда проекта испытывает кадровый "голод" - не хватает фронтенд разработчиков. В случае корпоративных проектов для внутреннего пользования особых красот на фронте не предполагается, поэтому вполне можно обойтись каким-нибудь MVC фреймворком, который управляется не запросами, а компонентами. В этом случае логику работы фронта обеспечивают компоненты на базовом ЯП, на котором написан бэкенд проекта, и сильный фронтенд разработчик не нужен. В крайнем случае можно добавить какую-то элементарную темизацию, которую может сделать фронтендер уровнем попроще или даже дизайнер. Одним из таких фреймворков как раз и является JavaServer Faces (JSF) из стека Java EE/Jakarta EE, а для улучшение функциональности фронта и чтобы внешний вид его не был совсем уж неприличным, можно подключить поверх JSF одну из разработанных для этого библиотек, например, Primefaces - https://www.primefaces.org/. Библиотека также имеет версии для работы поверх Angular, React или Vue, но для нашего случая подходит именно базовый вариант библиотеки, предназначенный для работы с JSF. Разумеется, библиотека может применяться на проектах с Java EE/Jakarta EE и без Spring Boot, но я этого делать не буду, интереснее будет посмотреть, как интегрировать библиотеку и как работать с ней на Spring Boot. Во-первых, Spring Boot ближе и понятнее большинству разработчиков, во-вторых, как я уже упоминал, интеграция Jakarta EE в Spring Boot улучшается постоянно и систематически.
Итак, перейдем к делу. Создадим обычный проект и добавим в него стандартные зависимости для web приложения на Spring Boot, можно воспользоваться инициализатором https://start.spring.io/, если это необходимо, или встроенным инициализатором, если у вас IntelliJ IDEA Ultimate. Конкретный набор основных и дополнительных зависимостей зависит от вашего проекта и, я уверен, что особого труда это не составит для вас, поскольку эту статью, скорее всего, будут читать уже достаточно опытные разработчики. Особо подчеркну одно исключение - на данном этапе разработки временно не подключайте spring-boot-starter-security, потому что сразу же с ходу правильно настроить его для совместной работы с Primefaces у вас, скорее всего, не получится. Многие компоненты сразу перестанут работать правильно, и вы застрянете на одном месте. Кроме того, диапазон компонентов в Primefaces довольно широкий, и многие из них реализованы под капотом совершенно по-разному, это вызовет затруднения. Рекомендую вначале довести все нужные страницы проекта до рабочего состояния без настроек безопасности, чтобы все страницы работали правильно, расположенные на них компоненты не выдавали ошибок и были оттестированы, и только затем приступайте к интеграции безопасности. Кроме того, в настоящее время довольно часто в Spring приложениях используются сторонние решения для системы безопасности, их мы вообще опустим в статье, возможно, когда-нибудь я к этому вернусь.
Теперь перейдем непосредственно к подключению зависимостей для Primefaces. Опять же, не буду утверждать, что приводимый мною вариант является единственным и неповторимым, поэкспериментируйте, возможно, вам подойдет другая комбинация зависимостей.
Прежде всего, подключим саму библиотеку Primefaces. Мне было интересно использовать самую последнюю версию на момент создания проекта. Кроме того, проект находится в стадии развития, поэтому также на всякий случай я подключил сразу же дополнительные расширения библиотеки, которые разрабатываются сообществом и могут добавить к функционалу много красивого и удобного. Может быть, не сразу, но когда-нибудь позже, я их использую.
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>12.0.0</version>
</dependency>
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>primefaces-extensions</artifactId>
<version>12.0.0</version>
</dependency>
Дополнительно сделаем себе еще кое-что приятное, подключим primeflex, это даст нам еще больше красивых стилей, иконок и так далее. Когда работаешь без фронтендера, это здорово помогает сделать позорный интерфейс менее позорным. Кстати - работайте без фронтендера только на внутренних проектах вашей компании, когда коллеги над тобой добродушно и необидно посмеются и скажут, что по сравнению с интерфейсами 1С получилось очень даже неплохо...
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>primeflex</artifactId>
<version>3.2.0</version>
</dependency>
Теперь вспомним, что библиотека написана поверх JSF, поэтому подключим пару зависимостей, без которых не обойтись никак. Это, собственно, главные зависимости, взятые из jakarta и jsf, без которых вообще ничего не заработает
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>10.0.0</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.joinfaces</groupId>
<artifactId>jsf-spring-boot-starter</artifactId>
<version>4.7.5</version>
</dependency>
Обратите внимание, что я закомментировал строку scoped для первой зависимости. В документации Primefaces обычно вы увидите ее раскоментированной. Делать так следует только в тех случаях, когда вы уверены, что будете разворачивать приложение в продуктиве на полноценных web серверах, которые действительно способны эту зависимость предоставить. Поскольку я использую стандартный для Spring web стартер с встроенным Tomcat, то эта строка нам только помешает. Вообще говоря, документацию Primefaces читать и использовать совершенно необходимо, поэтому дам ссылки на нее:
Но читать нужно с особой осторожностью, совершенно очевидно, что документация написана для проектов, основанных на Java EE/Jakarta EE, и очень многое в ней совершенно невозможно применить, если вы пытаетесь интегрировать Primefaces со Spring Boot в неизменном виде. По крайней мере, пока - пока что разработчиками не завершена полная интеграция Jakarta в Spring Boot, что уже было обещано со временем сделать. Мы еще не раз столкнемся с моментами в коде, которые необходимо будет переделать под текущее состояние дел.
Теперь упомяну еще одну зависимость. Она особая - вы не обнаружите необходимости в ней, когда будете разрабатывать проект в IntelliJ IDEA, так как IDE подтянет нужные связи и не скажет вам ничего плохо, если ее не будет. Однако, если вы попытаетесь собрать и запустить jar файл проекта, предполагая, что именно с его помощью вы и будете его позже деплоить в какой-то среде, вы обнаружите, что без этой зависимости проект вообще не запустится. Нужен будет именно толстый jar с нею, чтобы это заработало в таком виде.
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0.SP1</version>
</dependency>
Это именно старый вариант cdi-api, который где-то под капотом остался необходим для работы каких-то компонентов Primefaces, насколько я понял. Во всяком случае, несмотря на то, что описанные выше зависимости из jakarta и jsf мы используем новые, и кажется логичным использовать новую же версию пакета cdi-api, но так работать не будет! Я проверял, например, с зависимостью вот такой:
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>4.0.1</version>
</dependency>
Результат - jar приложение при запуске все равно попросит старые версии классов. Почему-то это меня не удивляет - бардак встречается повсюду, в любой технологии. Хотя, возможно, разработчики даже не предполагали, что какой-то сумасшедший будет пытаться интегрировать Primefaces со Spring, ведь на Западе в основном его используют с нормальным Glassfish, развернутым для Java EE/Jakarta EE. Но поскольку ранее я не работал с этим стеком, то решил сделать именно так, для меня главным было - показать возможности самих компонентов Primefaces и сделать простой проект для внутреннего использования без фронтендера. Поэтому у меня в проекте все по минимуму, без изысков.
Перейдем к структуре проекта. У меня она вот такая, но вы можете делать так, как вы считаете нужным, за исключением расположения xhtml файлов страниц, у меня используется расположение по умолчанию, но и его вы можете как-то поменять, в эту тему я не углублялся, мне все равно, где они лежат.
На скришоте вы также видите, что приложение имеет обычную структуру Spring и самый обычный стартовый класс, с некоторыми вольностями - я выделил отдельный пакет bean для специфических бинов компонентов, обслуживающих страницы приложения, логично, что это - отдельный слой и удобнее держать их отдельно.
После создания структуры заполним дефолтовый пустой файл index.xhtml минимальным содержанием, чтобы он смог хотя бы запуститься и мы бы поняли, что базовая структура приложения работает.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core">
</html>
Давайте еще добавим элементарный класс конфигурации для начальной страницы, например, вот такой:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
/*
* Used for forwarding to the index page by default. This will trigger
* the login.
*/
registry.addViewController("/").setViewName("forward:/index.xhtml");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
После чего мы можем запустить наше приложение и увидим пустую страницу. Все ОК, она и должна быть пустой, заполнять ее мы будем в следующей части статьи. Особенно обратим внимание на заголовки страницы - первые два относятся к jsf, затем мы видим primefaces и, наконец, строку для стандартной библиотеки тегов jsp - jstl, не все они могут пригодиться на каждой странице, зависит от используемых на ней компонентов.
В следующей части статьи будут разобраны создание стартовой страницы приложения с подробным разбором нескольких используемых на ней компонентов и классы самих компонентов и их настройка.
И в заключение хочу порекомендовать всем бесплатный вебинар от OTUS, где вы сможете познакомиться с одним из самых популярных проектов Spring, Spring Data. В том числе на уроке будут рассмотрены "продвинутые" его фишки, такие как проекции, спецификации и Example.
headliner1985
Подскажите, может есть ссылка на гитхаб исходного кода? Очень интересно как вы реализовали инжект спринг бинов в JSF бины. И как вообще идет работа со скоупами, возможно вы создавали кастомный спринг скоуп для @ViewScoped ?
AYamangulov Автор
Проект еще в работе, через пару-тройку месяцев буду выкладывать очищенную от чувствительных данных версию в github, пока придется смотреть на образцы кода из текста
@ViewScopedиз jakarta.faces.view, я стараюсь там, где это важно, в коде приводить соответствующие импорты, чтобы можно было по ним определить, где что применялось из аннотаций. Spring прекрасно понимает эту аннотацию, инжекты делаю через конструктор аннотацией @Inject. Но там, где очередь доходит до работы с репозиториями, можно пользоваться возможностями Spring. IntelliJ IDEA поругается на инжекцию бинов репозиториев в бины jakarta или javax, но все равно скушает их и приложение будет работать c БД нормально. В продолжениях к статье будет еще много примеров. Вообще продолжений будет несколько - так как хочется показать работу с многими компонентами, в них есть особенности применения, которые сразу так с ходу не понять, и поэтому полезно будет поделиться опытом. Буквально на днях будет вторая часть статьи, там уже будут кое-какие примеры бинов компонентов.