На первый взгляд сборка Maven выглядит магией — мы выполняем простую команду и запускается целый жизненный цикл, состоящий из фаз, к каждой из которой привязан свой плагин и goal (иногда не один).
Давайте разберемся, что такое фазы, голы, и как они соотносятся между собой.
Как соотносятся phases и goals
Сборку с помощью Maven можно сравнить с обедом из нескольких блюд: в нем есть закуска, первое блюдо, второе блюдо, напиток и десерт — всё это фазы (phases), а все вместе — жизненный цикл. Второе блюдо может быть котлетой, а может рыбой. Конкретика задается с помощью goal (конкретная команда конкретного плагина). Например, на фазе compile по умолчанию выполняется compiler:compile — это goal с названием compile плагина compiler. Эта команда компилирует исходники. А десерт может быть шарлоткой, это тоже конкретный goal, например deploy:deploy фазы deploy.
Сложность состоит в том, что набор умолчаний в Maven велик — согласно документации, в жизненном цикле много зарезервированных фаз, в которых ничего не выполняется (например, фаза validate). Эти фазы подразумевают какой-то смысл, но зарезервированы на тот случай, если программист захочет к ним привязать какое-то свое действие.
Конкретно на фазе validate должна проверяться корректность проекта, но как именно, решает программист, если ему это надо. (А если не надо, фаза validate пропускается). Ниже мы попробуем привязать к этой фазе простейшее действие.
Есть также фазы, которые, наоборот, выполняются по умолчанию — например, фаза compile. К ней по умолчанию привязан свой плагин и goal, как уже упоминалось выше.
Узнать, что именно происходит при сборке, можно, например, запустив сборку — все goals выведутся в консоль.
Можно также вывести все goals данной фазы так:
mvn help:describe -Dcmd=<фаза>
Попробуем запросить все goal фазы compile:
mvn help:describe -Dcmd=compile
Ответ такой:
compile' is a phase corresponding to this plugin: org.apache.maven.plugins:maven-compiler-plugin:3.6:compile
То есть на фазе compile по умолчанию выполняется maven-compiler-plugin. Через двоеточие указана goal, она называется compile.
Default build lifecycle
Запустим сборку, указав фазу install:
mvn install
Обратите внимание, что pom.xml практически пустой — в нем не перечислены никакие фазы и goals, они все привязаны по умолчанию к сборке jar-файла:
<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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ru.sysout</groupId> <artifactId>example-maven-project</artifactId> <version>1.0</version> </project>
При этом в pom.xml даже не указано, что надо собрать jar-файл — этот формат также подразумевается по умолчанию.
Вывод в консоль показывает, какие плагины и голы выполняются при сборке:
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ example-maven-project --- [WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] Copying 0 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ example-maven-project --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ example-maven-project --- [WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\Code\sysout\example-maven-project\src\test\resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ example-maven-project --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ example-maven-project --- [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ example-maven-project --- [INFO] Building jar: C:\Code\sysout\example-maven-project\target\example-maven-project-1.0.jar [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ example-maven-project --- [INFO] Installing C:\...\.m2\repository\ru\sysout\example-maven-project\1.0\example-maven-project-1.0.jar [INFO] Installing C:\...\.m2\repository\ru\sysout\example-maven-project\1.0\example-maven-project-1.0.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS
Как видно, по очереди выполняется:
- копирование папки ресурсов main\resources
- компиляция исходников из папки main\java
- копирование тестовых ресурсов test\resources
- компиляция исходников из папки test\java
- далее происходит выполнение тестов
- упаковка в jar и
- копирование jar файла в .m2 папку на локальном компьютере.
Есть в жизненном цикле еще фаза deploy — развертывание на сервер, эта последняя фаза тут не выполнялась.
Жизненный цикл (lifecycle) сборки — это последовательность фаз (phases).
Чтобы посмотреть, какой реально POM-получился (эффективный POM), можно набрать в консоли команду (будет выведен огромный реальный POM-файл):
mvn help:effective-pom // вывод в консоль mvn help:effective-pom -Doutput=file.xml //вывод в file.xml
Выполнение предыдущих фаз
Обратите внимание, что мы указали одну фазу install, но выполняется целый жизненный цикл со всеми предыдущими фазами, фаза install в этом цикле последняя.
Чтобы только скопировать ресурсы и скомпилировать проект, выполняем:
mvn compile
Чтобы, помимо этого, выполнить тесты и упаковать проект в jar, выполняем:
mvn package
(при этом выполнится компиляция и другие предваряющие package фазы).
Чтобы развернуть проект на сервере, выполним:
mvn deploy
Все предыдущие фазы, включая install, также при этом выполнятся. В pom.xml при этом должна быть указана конфигурация с данными сервера, куда деплоить проект.
В документации указана очередность фаз, она задана заранее. Там же указаны какие именно плагины и goals по умолчанию выполняются в каких фазах. Мы можем перезаписать или добавить свое действие к определенный фазе.
Попробуем добавить свое действие к двум фазам.
Как привязать к фазе свое действие
Во-первых, любое действие, которые вы добавляете к сборке, должно быть запрограммировано в виде Maven-плагина с goal (у плагина может быть и несколько goal, которые можно привязать к разным фазам). Этот плагин вы либо пишете сами, либо берете готовый. Готовых плагинов много.
Во-вторых, чтобы добавить goal к фазе, вы прописываете в pom.xml этот плагин и goal, чуть ниже показано, как. Также можно указать фазу в теге <phase> (можно, а не нужно, потому что для плагина уже может существовать умолчательная фаза, заданная при программировании плагина; но ее может и не быть). Добавленный goal будет выполнен после всех уже привязанных по умолчанию к фазе goal-ов.
Для примера возьмем плагин, разбираемый в документации по ссылке выше. Он выводит в консоль «Hello, world». Скомпилируйте и установите его в локальную папку .m2:
mvn install
Теперь можно добавить выполнение goal hello-maven-plugin:sayhi в фазы своего проекта, например в фазы compile и validate. Для этого в корень нашего почти пустого pom.xml дабавьте:
<build> <plugins> <plugin> <groupId>sample.plugin</groupId> <artifactId>hello-maven-plugin</artifactId> <version>1.0</version> <executions> <execution> <id>execution1</id> <phase>validate</phase> <goals> <goal>sayhi</goal> </goals> </execution> <execution> <id>execution2</id> <phase>compile</phase> <goals> <goal>sayhi</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
При этом сначала выполнится компиляция (так как этот goal уже привязан по умолчанию), потом напечатается «Hello, world».
Что касается фазы validate, то поскольку по умолчанию к этой фазе ничего не привязано, выполнится только наш goal sayhi (причем в самом начале, так как validate — первая фаза согласно документации).
В консоли мы увидим:
[INFO] Building example-maven-project 1.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- hello-maven-plugin:1.0:sayhi (execution1) @ example-maven-project --- [INFO] Hello, world. [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ example-maven-project --- [WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] Copying 0 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ example-maven-project --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- hello-maven-plugin:1.0:sayhi (execution2) @ example-maven-project --- [INFO] Hello, world. ........................................................
Кстати, наш goal можно выполнить и отдельно из командной строки:
mvn sample.plugin:hello-maven-plugin:1.0:sayhi
Какие еще есть жизненные циклы Maven
Хотя в работе чаще используется default lifecycle, о котором мы говорили выше, он не единственный.
Всего жизненных циклов у Maven определено три:
- default — этот цикл отвечает за развертывание проекта (и все предыдущие, предваряющие развертывание действия).
- clean — отвечает за удаление файлов, оставшихся с предыдущей сборки. Удаляет папку target.
- site — отвечает за создание документации.
mvn clean install
Довольно часто указывают две фазы подряд:
mvn clean install
Да, это фазы, но они принадлежат разным циклам. Тут запускается два цикла по очереди — сначала цикл clean, а затем цикл default.
Мы не можем указать, например:
mvn validate install
Это фазы из одного и того же цикла default, они не могут быть выдернуты из цикла и выполнены вне его (помните правило выполнения всех предыдущих фаз).
Так что хотя многие и считают, что в примере mvn clean install указаны некоторые две выбранные фазы одного цикла, это ложная догадка, на самом деле тут выполняются два цикла, а не две фазы.
На мой взгляд самая понятная статья про Maven
Наконец то у меня все разложили ь по полочкам. Большое спасибо автору, очень доступно и понятно!
«К каждой фазе можно привязать несколько голов, а можно ни одного — тогда фаза просто пропускается при сборке.»
Я так понимаю, что есть фазы которые мы ни где не указываем, но они ни пропускаются. Или я ошибаюсь?
да, там ниже приведен пустой pom — много всего делается при указании фазы install, в том числе компиляция.
достань parent pom, он действительно большой и ты увидишь что там всё прописано, что выполняется. Причём каждый pom от него по умолчанию наследуется, поэтому и происходит такая магия. Ещё его полезно почитать, потому что там есть некоторые properties которыми иногда удобно пользоваться и т.д.
Лучшая статья по циклам и фазам. Очень благодарен автору