Наводим порядок в разработке ПО вместе с maven. Часть 5

Материал из DOM

Перейти к: навигация, поиск


Я продолжаю рассказ об maven, и о том, как он упрощает разработку программных продуктов, задавая четкий ритм и последовательность шагов, через которые проходит жизненный цикл проекта. В прошлый раз мы завершили рассказ о самой большой и известной части maven – управление зависимостями. Сегодня нас ждет продолжение, и мы поговорим о создании многомодульных проектов.

Но перед тем как мы начнем рассматривать методики разделения больших проектов на составляющих их части (модули) следует рассказать о том, как различные известные среды разработки (IDE) поддерживают maven. Я пользуюсь в своей практике java-программиста такой известной ide как intellij idea (http://www.jetbrains.com/). На момент написания статьи самая “свежая” версия idea – это восьмая, которая поставляется вместе с “пачкой” плагинов, среди которых есть и maven-плагин. К примеру, после запуска idea, на первом экране вам будет предложены различные стратегии создания проекта. Среди которых, помимо создания “с нуля” или извлечения проекта из cvs/svn, есть и импорт в idea проекта созданного с помощью maven. Для этого вы на экране приветствия idea выбираете пункт “Create New Project”, затем “Import project from external model” -> “maven”. На появившемся диалоговом окне с настройками импорта maven-проекта (см. рис. 1)

Изображение:maven_5_1.png

вам нужно только указать месторасположение файла pom.xml, да и отметить checkbox-ы “Create idea modules for aggregate projects” и “Automatically re-import on project opening”. Первая опция важна в случае, когда ваш maven-проект состоит из нескольких модулей (вторая часть этой статьи как раз и посвящена этой теме). Опция “Automatically re-import on project openning” нужна для того, чтобы избежать проблемы синхронизации проекта maven и проекта idea. Т.к. после импорта проекта idea создаст “пачку” собственных файлов описывающих проект (с расширениями ipr, iml, iws) и именно эти файлы будут использоваться idea, для компиляции проекта. Это значит, в файлах ipr, iws, iml будут храниться дубли настроек maven (тот же список зависимостей). А теперь представьте себе, что нужно добавить в проект новую библиотеку. И где вы будете это делать, в idea-проекте или в maven-проекте? Для того, чтобы избежать потенциальной несогласованности проектов, idea может каждый раз при открытии проекта выполнять его синхронизацию с мастер-копией в виде maven-проекта (следовательно, все правки проекта вы будете выполнять только в pom-файле). Еще на диалоговом окне импорта maven-проекта советую обратить внимание на кнопку “Advanced”. После ее нажатия появится диалоговое окно с дополнительными настройками maven. Так вы можете указать путь к установленному у вас на компьютере maven, или изменить путь к каталогу репозитория (по умолчанию он располагается в "с:\Documents and Settings\UserName \.m2\repository"). В любом случае после окончания импорта idea создаст для вас проект, повторяющий структуру maven-модулей, и подключит к проекту все необходимые библиотеки-зависимости. Приятно, что каждая библиотека будет представлена тремя файлами: собственно, jar-файл с самой библиотекой, затем файл с исходными кодами библиотеки и файл документации (javadoc). Завершив импорт проекта, вы получите в свое распоряжение специальное окошко “Maven projects” (см. рис. 2)

Изображение:maven_5_2.png

из которого вы можете инициировать различные фазы жизненного цикла maven (compile, test, install), также мы можете выполнять запуск целей плагинов. Например, на картинке 2 выделен плагин jetty и его goal (цель) jettu-run. Инициировав эту цель, я “в один клик” запустил веб-сервер jetty и развернул на нем свое веб-приложение. Из “мелких вкусностей” могу упомянуть возможность привязать горячие клавиши (клавиатурные сокращения), которые можно привязать к фазам проекта, можно привязать перезапуск того же веб-сервера как действие после компиляции проекта и многое другое. Помимо intellij idea поддержка maven есть и в eclipse. Так выберите меню “File -> Import->Maven Projects”, затем укажите расположение pom-файла с проектом и, вуаля, eclipse сгенерирует набор файлов для проекта (.settings, .classpath, .project). Так вы получаете в свое распоряжение набор инструментов повторяющих почти один в один те, что были доступны в среде intellij idea. И, хоть мне как человеку, предпочитающему idea, не приятно это говорить, но eclipse представляет очень удобный визуальный редактор pom-файла. Т.е. вы можете не только править xml-код “ручками” но и просматривать различные аспекты maven-проекта и редактировать их в графической форме, к примеру я показал на рис.3

Изображение:maven_5_3.png

как выглядит окно с основными настройками проекта (название группы артефактов, название артефакта-проекта, его версия). А на рис. 4 показано дерево зависимостей артефактов, т.е. от каких артефактов наш проект зависит напрямую, а какие артефакты были разрешены для нашего проекта через поиск транзитивных зависимостей. В прошлой статье, я рассказывал о таком maven-плагине как dependency и его цели dependency:tree, формировавшей на экране “дерево” зависимостей артефактов, т.е. какой артефакт “потянул” какой артефакт и так далее. Это же дерево, но в более красивой, графической форме вы можете увидеть на рис. 4.

Изображение:maven_5_4.png

Некоторым недостатком (хотя это как посмотреть) поддержки maven в eclipse является “некая недосказанность”, т.е. рассматривать редактор pom-файла как средство быстрого изучения maven не стоит. И предварительно придется проштудировать “мануал” с основной терминологией maven и тем, что она означает. Специально, чтобы закрыть тему про maven и среды разработки я покажу то, как можно выполнить генерацию проекта для idea или для eclipse, что называется “вручную”. Генерацию проекта выполняет команда: “m2 idea:idea” или “m2 eclipse:eclipse”. Но мало вызвать плагин - нужно еще его правильно настроить. C основами настройки плагинов мы уже сталкивались ранее, во второй статье серии, когда я рассказывал о жизненном цикле проекта и плагинах, привязанных к фазам этого цикла. Maven позволяет по аналогии настроить и плагины, которые не участвуют непосредственно в жизненном цикле проекта, например, плагин idea и eclipse:

<build>
 <finalName>myapp</finalName>
  <plugins>
   <plugin>
    <artifactId>maven-idea-plugin</artifactId>
    <version>2.3-SNAPSHOT</version>
    <configuration>
      <downloadSources>true</downloadSources>
      <downloadJavadocs>false</downloadJavadocs>
      <dependenciesAsLibraries>true</dependenciesAsLibraries>
      <useFullNames>false</useFullNames>
     </configuration>
   </plugin>
 </plugins>
</build>

Настройки плагина “idea” очевидны: каждая из зависимостей будет оформлена как отдельная библиотека, для каждой из библиотек будут загружены исходные коды (но не документация). Получившийся проект полностью готов к использованию в среде idea. Написав последний абзац о поддержке maven в idea и eclipse, я не удержался и решил проверить как с поддержкой maven в еще одной известной (хотя у меня с ней только “шапочное” знакомство) среде разработки netbeans (http://www.netbeans.org/). И оказалось, что помимо ожидаемой функции импорта maven-проекта в среду netbeans, есть еще и поддержка создания “с нуля” maven-проекта. Так выбрав пункт меню File -> New project -> Maven вы увидите диалоговое окно создания нового проекта на основании следующих заготовок: Web Application, EJB module, Enterprise Application. Как видите, поддержка maven есть в наиболее популярных средах разработки и это только радует.

До сего момента я упорно отмалчивался о том, как организовать много-модульный проект maven. Т.е. проект, состоящий из нескольких частей, когда та же бизнес-логика должна быть размещена отдельно от веб-модуля, формирующего веб-интерфейс приложения. Формально, мульти-модульный проект - это каталог, внутри которого находится pom-файл, содержимого которого сводится не к перечислению библиотек-зависимостей, а просто содержит перечисление модулей. Каждый из модулей проекта - это еще один подкаталог внутри родительского каталога, содержащий файл pom с описанием места, которое данный модуль занимает среди остальных модулей проект (т.е. того, от каких других модулей он зависит), а также перечня обычных зависимостей библиотек и плагинов. Очевидно, что каждый из модулей имеет собственный набор плагинов, привязанных к определенным фазам жизненного цикла проекта и модуля в его составе. Давайте создадим классический проект ear-приложения из трех модулей: ejb, war, ear. В практике количество модулей всегда больше, чем количество автономных частей приложения. Так модуль вполне может иметь пустой набор исходных кодов или ресурсов, и не содержать ни java-кода, ни html-страниц, ничего. Такой модуль-пустышка ценен тем, что внутри его мы описываем набор библиотек-зависимостей (например, библиотеки нужные для работы hibernate). А затем другой модуль (например, ejb) ссылается на модуль-пустышку и получает в свое распоряжение все зависимости, описанные внутри модуля-библиотеки. Такой подход необходим для того, чтобы избежать дубляжей зависимостей. К примеру, у вас в проекте два модуля ejb и для каждого из них нужна библиотека hibernate. Если вы будете описывать составляющие hibernate зависимости по отдельности в каждом из двух модулей, то очень скоро станет вопрос о поддержании их согласованностей, т.е. одинаковых списков составляющих hibernate артефактов и номеров их версией. Более подробно к вопросу создания модулей-пустышек я вернусь позже. А пока попробуем создать многомодульный проект и начнем с того, что внутри каталога testmultimodule я размещу pom файл проекта следующего вида:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>${myapp.groupid}</groupId>
  <artifactId>main</artifactId>
  <packaging>pom</packaging>
  <version>${myapp.version}</version>
  <name>main</name>
  <modules>
   <module>application</module>
   <module>business-logic</module>
   <module>web-interface</module>
  </modules>
  <build>
   <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          < source > 1.5 < / source > <!-- пробелы добавлены специально из-за особенностей форматирования mediawiki -->
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
   </pluginManagement>
  </build>
  <!-- Project properties -->
 <properties>
   <myapp.groupid>test01</myapp.groupid>
   <myapp.version>1.0</myapp.version>
   <myapp.finalname>${artifactId}-${myapp.version}</myapp.finalname>
 </properties>
</project>

Если вы вспомните примеры файлов pom, которые я приводил в прошлых статьях серии, то сразу увидите отличия. Так даже указание того, к какой группе артефактов относится мой проект, указано каким-то необычный образом: <groupId>${myapp.groupid}</groupId>. Что означают все эти значки “$” и фигурных скобок. Все дело в том, что раз мы создаем мульти-модульный проект, состоящих из нескольких pom-файлов, то нужно с самого начала позаботиться о том, чтобы избежать дубляжей. К примеру, название проекта, его версия будет повторяться во всех четырех (одном родительском и трех дочерних) проектах. А раз так, то почему бы мне не создать специальные переменные placeholder-ы, на которые можно сослаться в любом месте любого из pom-файлов. И действительно, в самом низу файла проекта внутри секции “properties” я создал три переменные “myapp.groupid”, “myapp.version” и “myapp.finalname”. На эти имена переменных я и ссылаюсь при объявлении maven-координат проекта (подробнее об maven-координатах см. часть 4). Обратите внимание на то, что значение элемента packaging равно pom, а не jar или ear. Считается, что сам многомодульный проект не производит никакого конечного продукта, а всего лишь служит для агрегации в единое целое других модулей, а вот они и производят конечный продукт (те же ejb, war, ear-модули). Есть правило, что настройки, определенные в родительском pom-файле, распространяются и на дочерние файлы. Этим я решил воспользоваться для того, чтобы не настраивать для каждого из модулей в отдельности параметры компиляции. Так я создал элемент build, внутри которого определил параметры для плагина “maven-compiler-plugin”, в частности версию используемого компилятора. Если вы хотите узнать больше о настройке плагинов, привязанных к фазам жизненного цикла, то обратитесь ко второй статье серии. Самое главное в файле проекта это перечисление списка модулей, которые его образуют, и делается это с помощью элемента modules. Как я и обещал, у меня получилось три дочерних модуля: business-logic (ejb-бины с логикой работы приложения), модуль web-interface хранит набор html-страниц, а модуль application служит для объединения предыдущих двух модулей в единое целое. Вдумчивый читатель тут же спросит: а важен ли порядок, под которым я поместил имена модулей внутри элемента modules? Ведь модули зависят друг от друга и очевидно, что сначала нужно выполнить компиляцию модуля с бизнес-логикой и только затем модуля с веб-интерфейсом, а затем результаты их работы разместить внутри ear-модуля. Формально, порядок размещения не важен, т.к. внутри файлов pom, описывающих дочерние модули вы будете явно декларировать список нужных для них зависимостей, а зависимость это не только “обычная” библиотека, но и модуль проекта. Так что maven самостоятельно перед запуском проекта на выполнение отсортирует модули в нужном порядке. Теперь перейдем к созданию дочерних подпроектов, и первым шагом я создам подкаталог business-logic со следующей структурой: подкаталоги src/main/java и src/main/resources (каталог для тестов я решил пропустить). Также внутрь подкаталога business-logic я поместил pom-файл модуля:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
 <parent>
 <artifactId>main</artifactId>
  <groupId>${myapp.groupid}</groupId>
  <version>${myapp.version}</version>
 </parent>
<groupId>${myapp.groupid}</groupId>
 <artifactId>business-logic</artifactId>
 <packaging>ejb</packaging>
 <version>${myapp.version}</version>
 <name>ejb container</name>
 
 <dependencies>
  <dependency>
   <groupId>org.apache.geronimo.specs</groupId>
   <artifactId>geronimo-ejb_3.0_spec</artifactId>
   <version>1.0.1</version>
   </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate</artifactId>
   <version>3.2.6.ga</version>
  </dependency>
 </dependencies>
  <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-ejb-plugin</artifactId>
    <configuration> 
      <ejbVersion>3.0</ejbVersion>
    </configuration>
  </plugin>
  </plugins>
  </build>
</project>

Для того, чтобы maven знал о том, что данный модуль “не просто так”, а является дочерним по отношению к другому проекту, то я в самом начале pom-файла разместил ссылку на родительский модуль внутри элемента parent. Здесь и далее при описании группы артефакта модуля и его версии я использую объявленные в родительском pom-файле переменные ${myapp.groupid} и ${myapp.version}. Что касается списка зависимостей модуля от внешних библиотек, то я подключил только hibernate и особую api-библиотеку geronimo-ejb_3.0_spec. Внутри этой библиотеки находятся описания аннотаций нужных для разработки ejb-компонентов (@Stateless, @Remote и т.д.). Полезным будет сказать, что в рамках проекта geronimo разработаны api-спецификации для многих других java-стандартов (geronimo-jms_1.1_spec, geronimo-servlet_2.5_spec, geronimo-jta_1.1_spec, geronimo-jaxws_2.1_spec …). Завершающим штрихом будет настройка maven, так чтобы он знал, что мы разрабатываем свой код внутри модуля business-logic согласно третьей редакции спецификации ejb. И делается это через настройку специального maven плагина maven-ejb-plugin и его параметра ejbVersion. На этом я заканчиваю рассказ о создании ejb-слоя приложения, а что касается веб-интерфейса (модуля web-interface), то это тема следующей статьи.

Subscribe Now!

 

ObMachine projects & articles (java, flash, flex, php, ...)  -- black-zorro.com