Получить версию артефакта Maven во время выполнения
Я заметил, что в JAR-файле артефакта Maven атрибут project.version включен в два файла:
META-INF/maven/${groupId}/${artifactId}/pom.properties
META-INF/maven/${groupId}/${artifactId}/pom.xml
Есть ли рекомендуемый способ чтения этой версии во время выполнения?
13 ответов
Вам не нужно обращаться к файлам, относящимся к Maven, чтобы получить информацию о версии любой данной библиотеки / класса.
Вы можете просто использовать getClass().getPackage().getImplementationVersion()
получить информацию о версии, которая хранится в.jar-файлах MANIFEST.MF
, К счастью, Maven достаточно умен. К сожалению, по умолчанию Maven также не записывает правильную информацию в манифест!
Вместо этого нужно изменить <archive>
элемент конфигурации maven-jar-plugin
установить addDefaultImplementationEntries
а также addDefaultSpecificationEntries
в true
, как это:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</plugin>
В идеале эту конфигурацию следует поставить в компанию pom
или другой базовый помп.
Подробная документация <archive>
элемент можно найти в документации архива Maven.
Чтобы продолжить ответ выше, для .war
Артефакт, я обнаружил, что должен был применить эквивалентную конфигурацию к maven-war-plugin
, скорее, чем maven-jar-plugin
:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</plugin>
Это добавило информацию о версии в MANIFEST.MF
в проекте .jar
(включен в WEB-INF/lib
из .war
)
Вот метод для получения версии из pom.properties, отступая к получению ее из манифеста
public synchronized String getVersion() {
String version = null;
// try to load from maven properties first
try {
Properties p = new Properties();
InputStream is = getClass().getResourceAsStream("/META-INF/maven/com.my.group/my-artefact/pom.properties");
if (is != null) {
p.load(is);
version = p.getProperty("version", "");
}
} catch (Exception e) {
// ignore
}
// fallback to using Java API
if (version == null) {
Package aPackage = getClass().getPackage();
if (aPackage != null) {
version = aPackage.getImplementationVersion();
if (version == null) {
version = aPackage.getSpecificationVersion();
}
}
}
if (version == null) {
// we could not compute the version so use a blank
version = "";
}
return version;
}
Если вы используете Spring Boot, вы можете использовать класс BuildProperties.
В качестве примера возьмите следующий фрагмент из нашего класса конфигурации OpenAPI:
@Configuration
@RequiredArgsConstructor // <- lombok
public class OpenApi {
private final BuildProperties buildProperties; // <- you can also autowire it
@Bean
public OpenAPI yourBeautifulAPI() {
return new OpenAPI().info(new Info()
.title(buildProperties.getName())
.description("The description")
.version(buildProperties.getVersion())
.license(new License().name("Your company")));
}
}
Я использую maven-assembly-plugin
для моей maven упаковки. Использование Apache Maven Archiver в ответе Joachim Sauer также может работать:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
<executions>
<execution .../>
</executions>
</plugin>
Поскольку архиватор является одним из общих компонентов maven, он может использоваться несколькими подключаемыми модулями сборки maven, что также может привести к конфликту, если появятся два или более подключаемых модуля, включая archive
Конфигурация внутри.
Я знаю, что это очень поздний ответ, но я хотел бы поделиться тем, что я сделал по этой ссылке:
Я добавил приведенный ниже код в pom.xml:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
И этот Advice Controller, чтобы получить версию как атрибут модели:
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice
public class CommonControllerAdvice
{
@Autowired
BuildProperties buildProperties;
@ModelAttribute("version")
public String getVersion() throws IOException
{
String version = buildProperties.getVersion();
return version;
}
}
Простое решение, совместимое с Maven и работающее для любого (в том числе и стороннего) класса:
private static Optional<String> getVersionFromManifest(Class<?> clazz) {
try {
File file = new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
if (file.isFile()) {
JarFile jarFile = new JarFile(file);
Manifest manifest = jarFile.getManifest();
Attributes attributes = manifest.getMainAttributes();
final String version = attributes.getValue("Bundle-Version");
return Optional.of(version);
}
} catch (Exception e) {
// ignore
}
return Optional.empty();
}
Чтобы запустить это в Eclipse, а также в сборке Maven, вы должны добавить addDefaultImplementationEntries
а также addDefaultSpecificationEntries
Помните записи, как описано в других ответах, затем используйте следующий код:
public synchronized static final String getVersion() {
// Try to get version number from pom.xml (available in Eclipse)
try {
String className = getClass().getName();
String classfileName = "/" + className.replace('.', '/') + ".class";
URL classfileResource = getClass().getResource(classfileName);
if (classfileResource != null) {
Path absolutePackagePath = Paths.get(classfileResource.toURI())
.getParent();
int packagePathSegments = className.length()
- className.replace(".", "").length();
// Remove package segments from path, plus two more levels
// for "target/classes", which is the standard location for
// classes in Eclipse.
Path path = absolutePackagePath;
for (int i = 0, segmentsToRemove = packagePathSegments + 2;
i < segmentsToRemove; i++) {
path = path.getParent();
}
Path pom = path.resolve("pom.xml");
try (InputStream is = Files.newInputStream(pom)) {
Document doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(is);
doc.getDocumentElement().normalize();
String version = (String) XPathFactory.newInstance()
.newXPath().compile("/project/version")
.evaluate(doc, XPathConstants.STRING);
if (version != null) {
version = version.trim();
if (!version.isEmpty()) {
return version;
}
}
}
}
} catch (Exception e) {
// Ignore
}
// Try to get version number from maven properties in jar's META-INF
try (InputStream is = getClass()
.getResourceAsStream("/META-INF/maven/" + MAVEN_PACKAGE + "/"
+ MAVEN_ARTIFACT + "/pom.properties")) {
if (is != null) {
Properties p = new Properties();
p.load(is);
String version = p.getProperty("version", "").trim();
if (!version.isEmpty()) {
return version;
}
}
} catch (Exception e) {
// Ignore
}
// Fallback to using Java API to get version from MANIFEST.MF
String version = null;
Package pkg = getClass().getPackage();
if (pkg != null) {
version = pkg.getImplementationVersion();
if (version == null) {
version = pkg.getSpecificationVersion();
}
}
version = version == null ? "" : version.trim();
return version.isEmpty() ? "unknown" : version;
}
Если ваша Java-сборка помещает целевые классы где-то, кроме "target/classes", то вам может потребоваться настроить значение columnsToRemove.
Я потратил некоторое время на два основных подхода, и они не сработали для меня. Я использую Netbeans для сборки, может быть, там что-то еще происходит. У меня были некоторые ошибки и предупреждения от Maven 3 с некоторыми конструкциями, но я думаю, что их было легко исправить. Нет, важная персона.
Я нашел ответ, который выглядит понятным и простым в реализации в этой статье на DZone:
У меня уже есть подпапка resources/config, и я назвал свой файл: app.properties, чтобы лучше отражать то, что мы можем там хранить (например, URL поддержки и т. Д.).
Единственное предостережение заключается в том, что Netbeans выдает предупреждение о том, что среда IDE нуждается в фильтрации. Не уверен, где / как. Это не имеет никакого эффекта в этом пункте. Возможно, есть обходной путь для этого, если мне нужно пересечь этот мост. Удачи.
В моем приложении для весенней загрузки решение из принятого ответа работало, пока я недавно не обновил свой jdk до версии 12. Пробовал все остальные ответы и не мог заставить это работать.
В этот момент я добавил следующую строку к первому классу моего весеннего загрузочного приложения сразу после аннотации @SpringBootApplication
@PropertySources({
@PropertySource("/META-INF/maven/com.my.group/my-artefact/pom.properties")
})
Позже я использую приведенное ниже, чтобы получить значение из файла свойств в том классе, в котором я хочу использовать его значение и appVersion
получает версию проекта мне:
@Value("${version}")
private String appVersion;
Надеюсь, это кому-то поможет.
Самое изящное решение, которое я нашел, это user6019417: ссылка
Не требует никаких хаков со свойствами. Чтобы избежать проблем с битой ссылкой в будущем, я продублирую ее здесь:
YourClass.class.getPackage().getImplementationVersion();
И (если у вас еще нет файла манифеста в jar/war, для меня Maven от Intellij Idea уже включил их), вам также потребуется небольшое изменение в pom.xml:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
...
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
...
Пробовал все ответы выше, но у меня ничего не получилось:
- Я не использовал Весну
- Удалось поместить версию внутри манифеста, но
someClass.class.getPackage().getImplementationVersion()
возвращено значение null
Однако версия была добавлена кjar
имя файла, поэтому я смог найти файл jar, используя:
new File(ClassLoader.getSystemResource("").toURI()).getParentFile();
а затем извлеките его из имени файла.
Вариант Java 8 для EJB в файле war с проектом maven. Проверено на EAP 7.0.
@Log4j // lombok annotation
@Startup
@Singleton
public class ApplicationLogic {
public static final String DEVELOPMENT_APPLICATION_NAME = "application";
public static final String DEVELOPMENT_GROUP_NAME = "com.group";
private static final String POM_PROPERTIES_LOCATION = "/META-INF/maven/" + DEVELOPMENT_GROUP_NAME + "/" + DEVELOPMENT_APPLICATION_NAME + "/pom.properties";
// In case no pom.properties file was generated or wrong location is configured, no pom.properties loading is done; otherwise VERSION will be assigned later
public static String VERSION = "No pom.properties file present in folder " + POM_PROPERTIES_LOCATION;
private static final String VERSION_ERROR = "Version could not be determinated";
{
Optional.ofNullable(getClass().getResourceAsStream(POM_PROPERTIES_LOCATION)).ifPresent(p -> {
Properties properties = new Properties();
try {
properties.load(p);
VERSION = properties.getProperty("version", VERSION_ERROR);
} catch (Exception e) {
VERSION = VERSION_ERROR;
log.fatal("Unexpected error occured during loading process of pom.properties file in META-INF folder!");
}
});
}
}