Модульная надстройка для JasperReports: откуда взялся Jasper и почему он так устроен. Часть 1

—

от автора

Исследовать такие масштабные и сложные продукты, как JasperReports, а уж тем более создавать для него собственный практичный инструмент — явно не тренд 2026 года, когда лайки в медиа и звёзды на GitHub достаются очередному ИИ-репозиторию, написанному другим ИИ для третьих ИИ.

Так что текст и сама работа могут показаться белыми воронами. Это мой личный проект — без претензий на «новое слово» в разработке, но точно имеющий полное право занять пару строк в зависимостях самых разных проектов. Описываемая библиотека — попытка глубокого обобщения опыта и личный вызов, ценный для каждого разработчика: не просто писать код, а создать то, чем будут пользоваться в комьюнити. Ну и строчка в резюме, конечно.

Впрочем, часть мысли из первого абзаца тут тоже актуальна: по разным данным, заметная доля трафика к разработческим сайтам и медиа уже исходит от ИИ-агентов, так что этот текст и для них — эти парни тоже теперь проводники тех самых строчек в чужие проекты.

К слову сказать, моя первая статья, посвящённая версии 1.0 библиотеки, в русскоязычном сегменте на момент написания уже занимает первую строчку в поиске Google по банальнейшему запросу «работа с jasperreports».

Сейчас же речь пойдёт про версию 3.0.0 библиотеки jasper-modular — полгода работы с первого релиза сняли различные шероховатости и убрали некоторые малозаметные уязвимые места.

Но сначала — немного предыстории.

С JasperReports я работал на нескольких проектах, и концепция, о которой пойдёт речь, рождалась в муках — из накопленных граблей. Но на работе заниматься отдельным инструментом для репортинга, конечно, было некогда: его пришлось делать в личное время.

Текст получился длинным, поэтому выходит серией. Эта часть — не инструкция: она про то, откуда взялся Jasper, почему он устроен именно так и что с ним происходит сейчас. Дальше будут идеи, на которых держится мой подход, а за ними практика: как подключить библиотеку и работать с ней, с кодом и шаблонами.

Откуда взялся Jasper

В самом начале нулевых румынский разработчик Теодор Данчу оценивал большой Java-проект, где нужно было формировать и печатать много сложных документов, и обнаружил, что готовые решения проекту не по карману. Опыт с виндовыми генераторами отчётов вроде Crystal Reports у него был, и он решил, что сделать похожий инструмент на Java будет не так уж сложно. Это его собственные слова из интервью 2005 года.

Задачу проекта он тогда же описал: движок можно встроить в любое Java-приложение, а главная его цель — постраничные документы, готовые к печати. То есть с самого начала это был инструмент для документов, а как он впишется в слои приложения, в эту задачу не входило.

В мире Crystal Reports и его родни отчёт — это самодостаточный файл дизайна, который владеет всем: запросом к базе, полосами-бэндами, группировками, итогами, форматированием. В оригинальном туториале 2002 года так прямо и сказано: вся структура отчёта — это стопка секций от заголовка до итогов. И там же — что SQL-запрос можно написать прямо в описании отчёта. Отчёт был приложением в миниатюре, вещью в себе: по умолчанию он сам подключался к базе и сам выполнял свой запрос, кто бы его ни делал — разработчик или аналитик. В клиент-серверные девяностые это было нормально: приложения и так ходили в базу напрямую, а бизнес-логика жила либо в самом клиенте, либо в хранимых процедурах.

Зоопарк способов

У самодостаточности есть оборотная сторона. Раз отчёт живёт сам по себе, он должен уметь брать данные откуда угодно — и способов для этого со временем стало много.

Их было несколько с самого начала: в том же туториале 2002 года рядом с SQL-запросом внутри шаблона описана передача из кода готового ResultSet. Дальше способов становилось только больше: выборки по XML (с 2004-го) и JSON (с 2011-го), адаптеры данных, когда отчёт сам знает адрес базы и пароль к ней, проброс родительского датасорса в субрепорт, ручная передача параметров по цепочке субрепортов — и всё прочее, что Jasper накопил за двадцать пять лет. Ни один из них не главный, и каждый проект выбирает свой. Даже если запрос не держать в шаблоне, вариантов не меньше: собрать SQL в Java и подставить его в запрос шаблона через параметр, выполнить самому и отдать результат, достать данные через JPA и завернуть в бины. В итоге репортинг превращается в лес неведомых решений: в каждом новом проекте отчёты устроены по-своему, и разбираться приходится заново.

Самое больное место в этом зоопарке — субрепорты. Именно ими в Jasper собирают документ из блоков — сетка секций сама по себе плоская, а вставить в неё отдельно свёрстанный блок можно только субрепортом, — и вот тут начинается ад: сложный проброс параметров, их ручная привязка к типам и ручной же инжектинг, дизайн, парсинг данных внутри, плюс сложный сам механизм «подключения» субрепорта. На живом примере это выглядит так. Возьмём счёт с блоком позиций. Чтобы в этом блоке появилось одно новое поле, при передаче данных из Java его нужно прописать в трёх местах: в Java-коде, где собирается карта параметров, в корневом шаблоне, откуда оно пробрасывается в субрепорт, и ещё раз в самом субрепорте. Всю эту обвязку — объявления параметров в шаблонах, их проброс по цепочке и карту значений в Java — дальше я буду называть вайрингом (wiring). И во всех трёх местах имя поля — просто строка, которую никто не проверяет. Ошибся в одной букве — в PDF вместо значения появится «null» или пустая ячейка, и ни одной ошибки в логах. С SQL в шаблоне не легче: новую колонку нужно добавить в запрос, объявить в шаблоне поле с точно таким же именем и не ошибиться в типе — и об опечатке вы узнаете, только когда отчёт начнут формировать.

Это не критика

Здесь важно оговориться про мою позицию: Jasper я не критикую. Он не «плохо спроектированная библиотека для разработчиков», которую я пытаюсь исправить, — он хорошо спроектированный наследник совсем другой традиции. Отличный движок и технология, к которой я добавляю то, что кажется полезным для удобства разработчиков.

Тем более что в самом Jasper ту же проблему видели и пытались на неё ответить. Начиная с шестой версии в нём есть книги отчётов: вместо бэндов в отчёте стоят части, каждая часть — отдельный отчёт со своими страницами и даже своим размером листа, части можно вкладывать друг в друга, а к документу можно собрать оглавление. По сути это попытка собрать документ деревом. Книги работают и сейчас и вполне могли стать хорошим ответом. Но это дерево из документов, а не из блоков внутри страницы: шапку, таблицу и подвал одной страницы из частей не сложишь — книги с самого начала задуманы как сборка документа из целых страниц, с оглавлением и разными форматами листа. А контракта между кодом и шаблоном нет и там: части подключаются по путям к файлам, данные в каждую передаются руками, по строковым именам, — в официальном примере частям с данными по отдельности пробрасывают соединение с базой и нужные параметры. Это всё тот же вайринг, только уровнем выше.

Роли есть, границ нет

И зоопарк, и ручной вайринг растут из устройства самого Jasper. В разработке приложений с такой путаницей, где добыча данных, их передача и отображение смешаны, давно справляются разделением на слои: модель отдельно, представление отдельно, а между ними контроллер. Это MVC, и дело не в том, что до него тогда не додумались: самому паттерну к 2001-му было больше двадцати лет, а в том же году, что и Jasper, вышла версия 1.0 Struts — фреймворка для веб-приложений на Java, построенного как раз на MVC. Роли MVC в Jasper тоже есть с первого дня: шаблон рисует, датасорс несёт данные, движок заполняет. Данные снаружи, как мы видели, он тоже умел принимать с самого начала.

Нет трёх вещей: качественного маппинга между Java-объектами и шаблоном (контракт между моделью и шаблоном — строковый, ручной и никем не проверяемый), ограничений в выборе подходов (как инжектить, как передавать данные, как парсить данные и т. д.) и границ между слоями — вьюхе разрешено самой сходить в базу. Параметр в Java и параметр в XML совпадают только потому, что их одинаково напечатали руками.

Паттерн был намечен — не состоялась его сепарация: жёстко разделять слои тогда никто и не хотел. Авторы отчётов той эпохи воспринимали право шаблона самому ходить в базу не как дыру в границе, а как главное удобство. Да и средствами самого языка проверять такое разделение при компиляции тогда было нечем: дженерики, аннотации и annotation processing появились в Java только в 2004–2006-м — на них сейчас и держится библиотека. Потом бизнес-логика переехала в серверные слои приложения, а репортинг с ней не переехал — так и остался приросшим к базе.

Движок в 2026-м

Может быть, что-то изменилось со временем? Прошло двадцать пять лет. Что происходит с Jasper сейчас — коротко.

Движок жив и поддерживается: версия 7.0.8 вышла в августе 2026-го, уязвимости чинятся. С седьмой версии модули для веба и JPA переведены с javax на jakarta — Servlet 6.0 вместо 4.0, Persistence 3.1 вместо 2.2, а для старых приложений оставлены javax-варианты. А вот устройство ядра с тех пор в основе не менялось. Совместимость шаблонов Данчу сравнивает с документами Word: JRXML, созданный десять лет назад, должен открываться новым движком так же, как DOC 2003 года открывается в новом Office. То есть шаблон для них — это формат документа, а не код с API, и главная задача — чтобы шаблоны с многолетней историей продолжали работать.

При этом слой интеграции с приложениями — ничей. Spring выпилил поддержку Jasper ещё в пятой версии, в 2017-м. А когда в 2026-м пользователи спросили про поддержку Spring Boot 4, Данчу ответил, что так и не услышал, в чём конкретно проблема, а потом показал на примере, что под Spring Boot 4 движок работает, и закрыл тикет.

Для команды движка встраивание в приложения — чужой слой, и по их логике это правильно. Но и никто другой его не подхватил: по запросу «jasperreports spring boot» находятся сторонние статьи из блогов, в основном 2019–2023 годов, и учат они собирать отчёт вручную — компилировать, заполнять, экспортировать шаг за шагом. А одна из них до сих пор показывает JasperReportsPdfView — тот самый класс, который Spring удалил в 2017-м.

Итого: ядро — компиляция, заполнение, пиксель-перфект вёрстка, экспорт — работает надёжно четверть века, и строить на нём можно спокойно. Но факт остаётся фактом: разработчики унаследовали инструмент, чей центр тяжести — в другом мире, и никто по ту сторону не собирается его двигать — так что двигать будем сами.

Во второй части — чего ждёт от инструмента современный разработчик, и один стандарт и три идеи, на которых построена библиотека.

ссылка на оригинал статьи https://habr.com/ru/articles/1086756/