Профили Maven: объявление, активация, -P и activeByDefault
В pom.xml два профиля: dev с <activeByDefault>true</activeByDefault>, задающий адрес базы db.url, и it для интеграционных тестов. Разработчик запускает mvn verify -P it, сборка проходит, но в target/classes/app.properties вместо адреса базы лежит строка db.url=${db.url}. Профиль dev молча выключился: activeByDefault действует, только пока в этом POM не активирован никакой другой профиль.
Профиль Maven (Maven profile) — это именованный набор настроек POM внутри элемента <profiles>, который применяется поверх основной модели только при активации: вручную ключом -P или автоматически по условию из <activation> (свойство, версия JDK, операционная система, наличие файла). Профили позволяют одной сборке вести себя по-разному в разных окружениях без копий pom.xml. Примеры рассчитаны на Maven 3.9.x.
Зачем нужны профили Maven
Профили нужны, когда один и тот же проект надо собирать немного по-разному: с другими свойствами для окружения, с дополнительными проверками на CI, с медленными тестами или модулем только по запросу, с настройками под конкретную версию JDK или ОС. Вместо нескольких копий pom.xml различия описываются профилями, а нужный набор включается при запуске.
| Сценарий | Что меняет профиль | Как обычно включают |
|---|---|---|
| Окружения dev / test / prod | Свойства (адрес сервиса, уровень логирования), подставляемые в ресурсы | -P prod; dev — по умолчанию |
| Проверки на CI | Плагины статического анализа, покрытия, подписи артефактов | По свойству (-Dci) или -P ci в скрипте CI |
| Интеграционные тесты | Плагин failsafe, тестовая база, дополнительные зависимости | -P it |
| Опциональный модуль | <modules>: модуль входит в сборку только при активации | -P e2e |
| Разные JDK и ОС | Параметры компилятора, нативные библиотеки, пути | Автоматически: <jdk>, <os> |
Профиль меняет то, что получается на выходе сборки: jar, собранный с -P prod, отличается от собранного без него. Поэтому настройки, которые можно задать при запуске приложения (переменные окружения, внешний конфиг), лучше не зашивать в сборку — подробнее в разделах Профили Maven и Spring profiles и Почему не хранить пароли.
Как объявить профиль: profiles, profile, id
Профили объявляют в элементе <profiles> файла pom.xml: каждый <profile> обязан иметь уникальный <id>, по которому его включают ключом -P, может содержать условие <activation> и любые разделы, которые профилю разрешено переопределять (<properties>, <dependencies>, <build> и др.).
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<app.env>dev</app.env>
<db.url>jdbc:h2:mem:shop</db.url>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<app.env>prod</app.env>
<db.url>jdbc:postgresql://db.example.com/shop</db.url>
</properties>
</profile>
</profiles> Свойства профиля подставляются туда же, куда и обычные свойства из <properties>: в зависимости, настройки плагинов, фильтруемые ресурсы. Включение вручную:
mvn package -P prod # activate prod
mvn package -P prod,it # several profiles
mvn package -P '!dev' # deactivate dev (quote the ! in bash) В многомодульном проекте профиль из -P включается во всех модулях reactor, где профиль с таким id объявлен (в том числе через родительский POM). Внешний файл profiles.xml рядом с pom.xml, который встречается в старых статьях, был в Maven 2; Maven 3 его не поддерживает.
Что можно переопределить в профиле
Профиль в pom.xml может содержать почти те же разделы, что и сам POM: свойства, зависимости, модули, плагины, ресурсы, репозитории. Недоступны профилю координаты проекта, <parent> и часть настроек <build> — каталоги исходников и вывода, а также расширения сборки.
| Раздел | Можно в профиле POM? | Типичное применение |
|---|---|---|
properties | Да | Адрес БД, имя окружения, флаги плагинов |
dependencies, dependencyManagement | Да | Драйвер БД или тестовая библиотека только для одного окружения |
modules | Да | Дополнительный модуль, который собирается только по запросу |
build: plugins, pluginManagement, resources, testResources, filters, finalName, defaultGoal | Да | Включить проверки кода на CI, другой каталог ресурсов или файл фильтров |
repositories, pluginRepositories, distributionManagement, reporting | Да | Другой репозиторий для публикации |
build: sourceDirectory, testSourceDirectory, outputDirectory, testOutputDirectory, extensions | Нет | — |
groupId, artifactId, version, packaging, parent | Нет | — |
Сценарий «дополнительный модуль». Медленные сквозные тесты выносят в модуль shop-e2e-tests и подключают его к reactor только в профиле:
<!-- shop-parent/pom.xml -->
<profiles>
<profile>
<id>e2e</id>
<modules>
<module>shop-e2e-tests</module>
</modules>
</profile>
</profiles> Обычная mvn verify собирает три модуля магазина, а mvn verify -P e2e — четыре. Как устроены агрегация и reactor, разобрано в уроке Многомодульный проект Maven.
Сценарий «плагин только в профиле». Плагин, объявленный в <build><plugins> профиля, добавляется к плагинам основной модели; execution с тем же id объединяется с основным. Так в профиль it кладут maven-failsafe-plugin (зачем он нужен — в разделе surefire и failsafe, как писать executions — в разделе Привязка к фазе: executions).
Активация профиля: -P, activeByDefault, activation
Профиль Maven активируется тремя путями: явно ключом -P (или через <activeProfiles> в settings.xml), по умолчанию через <activeByDefault> или автоматически, когда выполнено условие <activation>: есть свойство, подходит версия JDK, ОС или существует файл. Явная деактивация -P '!id' выключает профиль, даже если его условие выполнено.
| Способ | Пример | Когда профиль активен |
|---|---|---|
| Вручную | mvn verify -P prod,it | Профиль указан в -P (через запятую) или в <activeProfiles> файла settings.xml |
| Деактивация | -P '!dev' или -P -dev | Никогда в этом запуске, даже при выполненном условии |
| По умолчанию | <activeByDefault>true</activeByDefault> | Если в этом же POM не активирован ни один другой профиль (см. заметку ниже) |
| Свойство задано | <property><name>ci</name></property> | Задано системное или пользовательское свойство с любым значением: -Dci |
| Свойство не задано | <property><name>!ci</name></property> | Свойства ci нет |
| Значение свойства | <property><name>env</name><value>ci</value></property> | -Denv=ci; значение !ci по документации Maven — свойство не задано или его значение не ci |
| Версия JDK | <jdk>25</jdk>, <jdk>[17,21)</jdk>, <jdk>!1.8</jdk> | Maven запущен на JDK, версия которой начинается с префикса, входит в диапазон или (с !) не начинается с префикса |
| ОС | <os><family>windows</family></os> | Семейство ОС (windows, unix, mac); можно также name, arch, version |
| Файл | <file><exists>${basedir}/local.properties</exists></file> | Файл есть (exists) или отсутствует (missing) |
Пример автоматической активации: профиль ci включается, когда задано свойство -Dci, а профиль legacy-jdk — на JDK от 17 включительно до 21 не включая:
<profile>
<id>ci</id>
<activation>
<property>
<name>ci</name>
</property>
</activation>
<!-- static analysis plugins, coverage etc. -->
</profile>
<profile>
<id>legacy-jdk</id>
<activation>
<jdk>[17,21)</jdk>
</activation>
</profile> Если в одном <activation> указано несколько условий, в Maven 3.2.2 и новее профиль активируется, только когда выполнены все (в более старых версиях хватало одного). Переменные окружения в Maven 3 доступны условию <property> как свойства с префиксом env. (например, env.CI). В пути <file> допустимы ${basedir}, системные свойства и переменные окружения, но не значения из <properties>. Проверяйте результат командой help:active-profiles, а не по памяти.
Неочевидный момент: activeByDefault выключается молча
Профиль с activeByDefault активен, только пока в том же POM не активирован ни один другой профиль — ключом -P или по условию. Команда mvn verify -P it из начала урока включает it и тем самым выключает dev: свойство db.url исчезает, и в фильтруемом ресурсе остаётся ${db.url}. То же произойдёт, если кто-то добавит профиль, который автоматически активируется по JDK. Надёжнее включать профиль по умолчанию отсутствием свойства: <property><name>!prod</name></property> (он активен, пока не задано -Dprod), или перечислять нужные профили явно: -P dev,it.
Нюанс: активация по свойству не видит <properties>
Условие <property> в Maven 3 проверяет системные свойства и свойства из командной строки (-D), но не значения из раздела <properties> POM. Запись <env>ci</env> в <properties> профиль с условием env=ci не включит — ни в основной модели, ни внутри другого профиля. Задавайте такие значения через -Denv=ci или файл .mvn/maven.config в корне проекта.
Профиль и ресурсы для разных окружений
Чтобы приложение получило настройки окружения, свойства профиля подставляют в фильтруемые ресурсы: профиль задаёт db.url, а Maven при копировании src/main/resources заменяет в файлах выражения ${db.url} значением из активного профиля. Как включить фильтрацию (<filtering>true</filtering>) и какие файлы ей подвергать, разобрано в разделе Раздел build: ресурсы и фильтрация.
С профилями dev и prod из раздела Как объявить профиль и включённой фильтрацией шаблон и результат выглядят так:
# src/main/resources/app.properties
app.env=${app.env}
db.url=${db.url}
# target/classes/app.properties after: mvn package
app.env=dev
db.url=jdbc:h2:mem:shop
# target/classes/app.properties after: mvn package -P prod
app.env=prod
db.url=jdbc:postgresql://db.example.com/shop Если свойств окружений много, их держат не в POM, а в отдельных файлах фильтров: профиль задаёт <build><filters><filter>src/main/filters/prod.properties</filter></filters></build>, и значения из этого файла тоже подставляются в фильтруемые ресурсы. Помните, что результат — разные артефакты: jar для prod нельзя проверить на тестовом стенде, не пересобрав его.
Профили в settings.xml и activeProfiles
Профили можно объявить и в ~/.m2/settings.xml, но там они ограничены: допустимы только <activation>, <properties>, <repositories> и <pluginRepositories>. Обычно там хранят настройки конкретной машины — адрес корпоративного репозитория или локальные пути, а профиль включают постоянно через <activeProfiles>.
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
<profiles>
<profile>
<id>company</id>
<repositories>
<repository>
<id>company-snapshots</id>
<url>https://nexus.example.com/repository/maven-snapshots/</url>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>company</activeProfile>
</activeProfiles>
</settings> Id из <activeProfiles> активирует профиль с таким именем при каждом запуске Maven на этой машине, как если бы его передали в -P. Всё, что влияет на результат сборки, держите в pom.xml, иначе у коллег и на CI проект соберётся по-другому. Остальное устройство settings.xml (зеркала, логины, путь к репозиторию) — в разделе settings.xml: зеркала, логины и путь к репозиторию.
Какие профили активны: help:active-profiles
Узнать, какие профили Maven реально применил, можно командой mvn help:active-profiles: для каждого модуля она выводит активные профили и источник каждого (POM или settings.xml). Полный список объявленных профилей с признаком активности даёт mvn help:all-profiles, а итоговую модель с применёнными профилями — mvn help:effective-pom с теми же ключами -P.
Примерный вид для модуля, родитель которого объявляет dev с activeByDefault:
mvn help:active-profiles -pl shop-web
Active Profiles for Project 'com.examclouds:shop-web:jar:1.0-SNAPSHOT':
The following profiles are active:
- dev (source: com.examclouds:shop-parent:1.0-SNAPSHOT) Для help:all-profiles примерный вид (формат зависит от версии maven-help-plugin):
mvn help:all-profiles -N
[INFO] Listing Profiles for Project: com.examclouds:shop-parent:pom:1.0-SNAPSHOT
Profile Id: dev (Active: true, Source: pom)
Profile Id: prod (Active: false, Source: pom)
Profile Id: e2e (Active: false, Source: pom) Чтобы проверить, что даст профиль, не запуская сборку, сравните mvn help:effective-pom и mvn help:effective-pom -P prod. Профиль, указанный в -P, но не найденный ни в одном POM, Maven 3.9.x отмечает предупреждением вида The requested profile "prd" could not be activated because it does not exist. — частая опечатка в CI-скриптах, которая не роняет сборку.
Профили Maven и Spring profiles
Профили Maven и профили Spring — разные механизмы с одинаковым названием: профиль Maven действует во время сборки и меняет сам артефакт, а профиль Spring действует при запуске приложения и выбирает конфигурацию внутри одного и того же артефакта. Их можно связать, но заменять один другим не стоит.
| Признак | Профиль Maven | Профиль Spring |
|---|---|---|
| Когда действует | При сборке (mvn package) | При запуске приложения |
| Где описан | <profiles> в pom.xml или settings.xml | application-{profile}.properties, аннотация @Profile |
| Как включить | -P prod, условие <activation> | spring.profiles.active=prod, переменная SPRING_PROFILES_ACTIVE |
| Результат | Для каждого окружения свой артефакт | Один артефакт, конфигурация выбирается на месте |
Для настроек окружения (адреса, логины, уровни логирования) обычно удобнее профили Spring или переменные окружения: один и тот же jar проходит тестовый стенд и уходит в прод. Профили Maven оставляют для того, что действительно меняет сборку: состав модулей, плагины проверок, зависимости под конкретную JDK.
Почему не хранить пароли в профилях
Пароли, токены и другие секреты не стоит класть в профили pom.xml: файл хранится в Git, а при mvn deploy публикуется в репозиторий артефактов вместе со всеми профилями. Если секрет ещё и подставляется фильтрацией в ресурсы, он оказывается внутри собранного jar.
Подводный камень: профиль prod с паролем уходит в репозиторий
Строка <db.password>secret</db.password> в профиле prod видна всем, у кого есть доступ к Git, и всем, кто скачивает ваш POM из Nexus или Maven Central. Удалить её из истории Git и из опубликованных версий гораздо сложнее, чем не добавлять. Пароль базы приложение должно получать при запуске (переменная окружения, хранилище секретов), а учётные данные для репозиториев Maven — из <servers> в settings.xml или из секретов CI.
Частые ошибки с профилями
Большинство проблем с профилями Maven молчаливые: сборка проходит, но не с теми настройками. Поэтому при любом сомнении первым делом запускайте help:active-profiles с теми же ключами, что и сборку.
| Симптом | Причина | Как исправить |
|---|---|---|
Профиль по умолчанию «пропал» после добавления -P | activeByDefault выключается, когда активирован любой другой профиль того же POM | Активировать по <property><name>!...</name></property> или перечислять профили явно |
Профиль с условием <property> не включается, хотя свойство есть в <properties> | Условие в Maven 3 видит только системные свойства и -D | Передать свойство через -D (-Denv=ci) или записать в .mvn/maven.config |
Профиль не включился, сборка прошла, в логе could not be activated because it does not exist | Опечатка в -P или профиль объявлен в другом POM | Сверить id с help:all-profiles |
Профиль с двумя условиями в <activation> не срабатывает | С Maven 3.2.2 нужны все условия сразу, а не одно | Разделить на два профиля или выполнить оба условия |
В ресурсе осталась строка ${db.url} | Профиль со свойством не активен или для каталога не включена фильтрация | Проверить help:active-profiles и <filtering>true</filtering> |
| Свойство из профиля не применилось, хотя профиль активен | То же свойство передано через -D: пользовательское свойство приоритетнее | Убрать -D из команды или CI-скрипта |
| Каталог исходников из профиля игнорируется, Maven сообщает о нераспознанном элементе | sourceDirectory и другие каталоги нельзя задавать в профиле (предупреждение или ошибка — зависит от версии) | Задать каталог в основном <build> или подключить исходники плагином |
| На проде работает сборка, проверенная на тесте, но с другим поведением | Артефакты собраны с разными -P и отличаются содержимым | Выносить настройки окружения из сборки в конфигурацию запуска |
Часто задаваемые вопросы
Как включить профиль Maven в IntelliJ IDEA?
В окне Maven раскройте узел Profiles и отметьте нужные профили флажками: IDEA передаёт их при импорте проекта и при запуске целей из этого окна. Профили, активные по условию, IDEA определяет сама. В конфигурации запуска Maven список профилей тоже можно задать вручную, как ключ -P.
Может ли один профиль включить другой?
Нет. Свойство из <properties> профиля не активирует другой профиль: условие <property> видит только системные свойства и -D (см. заметку в разделе Активация профиля). Если два профиля нужны вместе, дайте им общее условие, например одно свойство -Dci, или перечисляйте оба: -P ci,it.
Действуют ли профили родительского POM в модулях?
Действуют, но тоньше, чем обычное наследование. Если условие профиля, объявленного в родителе, выполнено при сборке модуля, профиль применяется к модели родителя, и модуль получает результат: свойства, зависимости, плагины. Сам профиль модулю не принадлежит. Откуда пришёл профиль, показывает help:active-profiles (source), итоговую модель — help:effective-pom в каталоге модуля.
Что приоритетнее: -Ddb.url или свойство из профиля?
Значение из командной строки. Пользовательское свойство -Ddb.url=... перекрывает и <properties> POM, и свойства активного профиля, поэтому забытый -D в CI-скрипте может молча отменить настройку профиля.
Как посмотреть, что изменит профиль, не запуская сборку?
Сравните итоговые модели: mvn help:effective-pom -Doutput=base.xml и mvn help:effective-pom -P prod -Doutput=prod.xml, затем любой утилитой сравнения файлов. Разница — ровно то, что добавляет профиль: свойства, зависимости, плагины, модули.
Практика по теме
Закрепите урок на задачах — у каждой есть уровень и оценка времени.
Все задачи модуля →
Комментарии