Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема зависимостей в теневых JAR (Uber JAR)
Теневой JAR (Shadow JAR или Uber JAR) — это специальный архив, который содержит не только код приложения, но и все его зависимости (библиотеки, которые приложение использует). Проблема зависимостей в теневых JAR — это набор сложных проблем, которые могут возникнуть при упаковке и распределении приложения таким образом.
Что такое Shadow JAR
// Обычная структура JAR
// myapp.jar
// ├── com/example/Main.class
// ├── META-INF/MANIFEST.MF
// └── lib/ (НЕ содержит зависимости)
// Shadow JAR (Uber JAR)
// myapp-all.jar
// ├── com/example/Main.class
// ├── org/apache/commons/... (Apache Commons)
// ├── com/google/... (Google Guava)
// ├── ... все зависимости встроены
// └── META-INF/MANIFEST.MF
Основные проблемы
1. Конфликт версий (Version Conflict)
Если две библиотеки зависят от разных версий одного пакета:
// pom.xml
<dependencies>
<!-- Библиотека A зависит от commons-io 2.10.0 -->
<dependency>
<groupId>org.apache</groupId>
<artifactId>libraryA</artifactId>
<version>1.0</version>
</dependency>
<!-- Библиотека B зависит от commons-io 2.8.0 -->
<dependency>
<groupId>org.apache</groupId>
<artifactId>libraryB</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
// В Shadow JAR обе версии присутствуют одновременно
// commons-io-2.10.0.jar
// commons-io-2.8.0.jar
2. Загрязнение пространства имён (Namespace Pollution)
// Обе библиотеки имеют класс с одинаковым полным названием
// libraryA: org/example/utils/StringHelper.class
// libraryB: org/example/utils/StringHelper.class
// В Shadow JAR второй класс перезаписывает первый!
// Это приводит к ошибкам выполнения (NoClassDefFoundError, LinkageError)
3. Проблема с изолированными классами (Sealed Packages)
// Если пакет запечатан (sealed package)
// org.apache.commons (all classes must come from commons.jar)
// И Shadow JAR содержит этот пакет из других JAR
// Возникает: java.lang.SecurityException:
// "sealed package org.apache.commons cannot be extended"
Практические примеры проблем
Пример 1: Конфликт классов логирования
// Две библиотеки используют разные версии SLF4J
// Library A: slf4j-api-1.7.30
// Library B: slf4j-api-2.0.0
// В runtime возникает:
// Exception in thread main java.lang.LinkageError:
// loader (instance of java.net.URLClassLoader):
// attempted duplicate class definition for name
Пример 2: Проблема с конфигурационными файлами
// Несколько библиотек используют META-INF/services/
// library-a: META-INF/services/org.example.Service
// содержит: com.example.ServiceImplA
// library-b: META-INF/services/org.example.Service
// содержит: com.example.ServiceImplB
// В Shadow JAR: второй файл перезаписывает первый!
// Это приводит к тому, что ServiceImplA никогда не загружается
// (важно для SPI - Service Provider Interface)
Решение 1: Relocation (Переименование пакетов)
<!-- pom.xml с maven-shade-plugin -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>org.apache.commons</pattern>
<shadedPattern>shade.org.apache.commons</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.guava</pattern>
<shadedPattern>shade.com.google.guava</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
// После relocation код будет использовать переименованные классы
// import shade.org.apache.commons.io.IOUtils;
Решение 2: Исключение конфликтующих зависимостей
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
Best Practices
- Избегай Shadow JAR когда возможно — используй правильное управление зависимостями
- Используй Relocation если Shadow JAR необходим
- Документируй исключения в pom.xml с комментариями
- Тестируй классы в runtime чтобы убедиться что правильные версии загружаются
- Используй Maven BOM (Bill of Materials) для согласования версий
Проблемы зависимостей в Shadow JAR — это серьёзная проблема, которая требует тщательного планирования и инструментов для её решения. Лучший подход — избегать Shadow JAR и использовать правильную архитектуру приложения с управлением зависимостями через Maven/Gradle BOM и правильное исключение конфликтов.