如何记录pom工件和版本
How to log the pom artifact and version
在ProjectA中,我在ClassA
中有一个MethodA
,在不同的项目中添加了ProjectA的jar作为Maven依赖,不同的项目是调用 MethodA
.
要求是
每当 ClassA
的 MethodA
被任何其他项目调用时,我们需要记录被调用的项目工件 ID 和版本,考虑到这些项目中添加了 ProjectA 依赖项 pom.xml .
注意
以下仅适用于自身项目 (ProjectA),创建 属性 文件并打开 maven 资源过滤
Create a property file
src/main/resources/project.properties
with the below content
version=${project.version}
artifactId=${project.artifactId}
Now turn on maven resource filtering
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
方法A
public class ClassA {
final static Logger logger = Logger.getLogger(ClassA.class);
public void MethodA{
final Properties properties = new Properties();
properties.load(this.getClassLoader().getResourceAsStream("project.properties"));
logger.info(properties.getProperty("version"));
logger.info(properties.getProperty("artifactId"));
}
}
在项目 B 中调用 MethodA 时,我在记录器中得到以下输出
version=${project.version}
artifactId=${project.artifactId} which is incorrect.
预期输出:
version = 1.0.0
artifactId = ProjectB
有没有更好的方法来记录调用项目工件 ID?如果MethodA
被ProjectC调用,我们要获取ProjectC的artifactid和version。
要求:我们有30多个项目从ProjectA调用MethodA,所以我们不应该对调用项目做任何更改。
方案一:Maven资源过滤
如果您将 POM 片段放在正确的 POM 部分,您的 POM 片段应该可以正确替换变量:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
您可以在 target/classes
文件夹中查看结果。在我通过向您的方法名称添加一个空参数列表 ()
并将无意义的 this.getClassLoader()
替换为 getClass().getClassLoader()
来修复您的错误伪代码之后,代码甚至可以编译并执行一些有意义的操作。在 post 之前,您是否曾在 Whosebug 等 public 平台上测试过某些东西?
import java.io.IOException;
import java.util.Properties;
public class ClassA {
public void methodA() throws IOException {
final Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("project.properties"));
System.out.println(properties.getProperty("version"));
System.out.println(properties.getProperty("artifactId"));
}
public static void main(String[] args) throws IOException {
new ClassA().methodA();
}
}
运行在 mvn compile
之后从 IntelliJ IDEA 中 运行 时的控制台日志(因为我们需要 Maven 处理资源并将它们复制到 target/classes
):
1.9.8-SNAPSHOT
util
或者你的模块名称和版本是什么。
如果您看到的是变量名,要么是您的 classpath 没有指向 JAR,而是指向源目录他们中的一些人忘记了资源过滤。在 class 路径中首先找到的文件将被加载。所以在多模块项目中,最好使用不同的文件名,否则或多或少就是抽奖,先找到哪个。
下一个问题是让您的方面或其他模块知道要加载哪个资源文件,以便更好地链接到 class 或包名称,以便其他模块能够从包名猜测资源文件。然后,您确实需要在模块之间进行干净的包名称分隔。我真的很想知道这是否值得。
解决方案 B:模板化 Maven 插件 + package-info.java
+ 自定义注解
另一个想法是使用资源过滤或类似 org.codehaus.mojo:templating-maven-plugin
的插件将版本直接替换为 package annotation values in a package-info.java
file,然后在 运行 时间内从包信息中简单地获取值.我用那个插件做了一个快速而肮脏的本地测试,它运行良好。我建议暂时保持简单,只需解决您的资源过滤问题。如果您需要我刚才描述的更通用的解决方案,请告诉我。
项目结构
更新: 我将侵入我的一个项目的快速解决方案提取到一个新的 Maven 多模块项目中,以便向您展示一个干净的解决方案,如下所示:
比如说,我们有一个包含 3 个子模块的父 POM:
annotation
- 包含要在 package-info.java
文件中的包上使用的注释。可以很容易地修改为也适用于 classes.
library
- 应用程序模块要访问的示例库
application
- 示例应用程序
您可以在 GitHub 上找到完整的项目:
https://github.com/kriegaex/SO_Maven_ArtifactInfoRuntime_68321439
项目目录结构如下:
$ tree
.
├── annotation
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── annotation
│ └── MavenModuleInfo.java
├── application
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── de
│ │ │ └── scrum_master
│ │ │ └── Whosebug
│ │ │ └── q68321439
│ │ │ └── application
│ │ │ └── Application.java
│ │ └── java-templates
│ │ └── de
│ │ └── scrum_master
│ │ └── Whosebug
│ │ └── q68321439
│ │ └── application
│ │ └── package-info.java
│ └── test
│ └── java
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── application
│ └── ModuleInfoTest.java
├── library
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── de
│ │ └── scrum_master
│ │ └── Whosebug
│ │ └── q68321439
│ │ └── library
│ │ └── LibraryClass.java
│ └── java-templates
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── library
│ └── package-info.java
└── pom.xml
请注意库和应用程序模块中的 src/java-templates
目录,其中包含 package-info.java
个文件。目录名称是 Templating Maven Plugin 的默认名称,使插件配置不那么冗长。
父 POM
<?xml version="1.0" encoding="UTF-8"?>
<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>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<modules>
<module>annotation</module>
<module>library</module>
<module>application</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
模块annotation
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>annotation</artifactId>
</project>
package de.scrum_master.Whosebug.q68321439.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MavenModuleInfo {
String groupId();
String artifactId();
String version();
}
模块library
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>library</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
package de.scrum_master.Whosebug.q68321439.library;
public class LibraryClass {}
请注意,以下文件需要位于 library/src/main/java-templates/de/scrum_master/Whosebug/q68321439/library/package-info.java
中。在这里你可以看到我们如何使用 Maven 属性在构建过程中通过 Templating Maven Plugin 将其替换为相应的值:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.Whosebug.q68321439.library;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
模块application
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>application</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package de.scrum_master.Whosebug.q68321439.application;
public class Application {
public static void main(String[] args) {}
}
请注意,以下文件需要位于 application/src/main/java-templates/de/scrum_master/Whosebug/q68321439/application/package-info.java
中。在这里你可以看到我们如何使用 Maven 属性在构建过程中通过 Templating Maven Plugin 将其替换为相应的值:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.Whosebug.q68321439.application;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
package de.scrum_master.Whosebug.q68321439.application;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
import de.scrum_master.Whosebug.q68321439.library.LibraryClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ModuleInfoTest {
@Test
public void test() {
String groupId = "de.scrum-master.Whosebug.q68321439";
MavenModuleInfo libMavenInfo = logAndGetMavenModuleInfo("Library Maven info", LibraryClass.class.getPackage());
assertEquals(groupId, libMavenInfo.groupId());
assertEquals("library", libMavenInfo.artifactId());
MavenModuleInfo appMavenInfo = logAndGetMavenModuleInfo("Application Maven info", Application.class.getPackage());
assertEquals(groupId, appMavenInfo.groupId());
assertEquals("application", appMavenInfo.artifactId());
}
private MavenModuleInfo logAndGetMavenModuleInfo(String message, Package aPackage) {
MavenModuleInfo moduleInfo = aPackage.getAnnotation(MavenModuleInfo.class);
System.out.println(message);
System.out.println(" " + moduleInfo.groupId());
System.out.println(" " + moduleInfo.artifactId());
System.out.println(" " + moduleInfo.version());
return moduleInfo;
}
}
运行 Maven 构建
现在 运行 Maven 构建通过 mvn clean test
:
(...)
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ application ---
[INFO] Surefire report directory: C:\Users\alexa\Documents\java-src\SO_Maven_ArtifactInfoRuntime_68321439\application\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running de.scrum_master.Whosebug.q68321439.application.ModuleInfoTest
Library Maven info
de.scrum-master.Whosebug.q68321439
library
1.0-SNAPSHOT
Application Maven info
de.scrum-master.Whosebug.q68321439
application
1.0-SNAPSHOT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec
(...)
识别来电者
假设所有的调用模块都实现了相同的scheme package info + special annotation,你可以这样打印调用者信息:
package de.scrum_master.Whosebug.q68321439.library;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
public class LibraryClass {
public void doSomething() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void doSomethingJava9() {
Class<?> callerClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
}
虽然 doSomething()
也适用于旧的 Java 版本(在 Java 8 上测试),但在 Java 9+ 上你可以使用 JEP 259 Stack-Walking API 作为doSomethingJava9()
中所示。在这种情况下,您不需要手动解析异常堆栈跟踪和处理异常。
解决方案 C:通过 URL classloader
识别调用 JAR
假设您使用我的示例项目并从应用程序模块调用库(就像在上一节中一样),打印 JAR 信息的一种快速而肮脏的方法是这样的:
将此方法添加到 LibraryClass
:
public void doSomethingClassLoader() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
// Cheap way of getting Maven artifact name - TODO: parse
System.out.println(
callerClass
.getClassLoader()
.getResource(callerStackTraceElement.getClassName().replaceAll("[.]", "/") + ".class")
);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
同样,在 Java 9+ 上,您可以使用 Stack-Walking API 使代码更好,见上文。
调用方法 Application
:
public class Application {
public static void main(String[] args) {
// new LibraryClass().doSomething();
// new LibraryClass().doSomethingJava9();
new LibraryClass().doSomethingClassLoader();
}
}
现在从命令行和 运行 使用 3 个不同的 class 路径构建 Maven 应用程序,指向
target/classes
目录
target
目录中的 JAR
- 本地 Maven 存储库中的 JAR
为了查看打印到控制台的信息类型:
$ mvn install
(...)
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\classes" de.scrum_master.Whosebug.q68321439.application.Application
file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/classes/de/scrum_master/Whosebug/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\application-1.0-SNAPSHOT.jar" de.scrum_master.Whosebug.q68321439.application.Application
jar:file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/application-1.0-SNAPSHOT.jar!/de/scrum_master/Whosebug/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;c:\Users\Alexa\.m2\repository\de\scrum-master\Whosebug\q68321439\application.0-SNAPSHOT\application-1.0-SNAPSHOT.jar" de.scrum_master.Whosebug.q68321439.application.Application
jar:file:/C:/Users/alexa/.m2/repository/de/scrum-master/Whosebug/q68321439/application/1.0-SNAPSHOT/application-1.0-SNAPSHOT.jar!/de/scrum_master/Whosebug/q68321439/application/Application.class
如你所见
- 在情况1中,您可以从项目路径间接推断出Maven工件,
- 在情况 2 中,您在 JAR 名称中看到工件 ID 和版本,在项目路径中间接看到组 ID,
- 在情况 3 中,您会在 JAR 名称中看到工件 ID 和版本,在 Maven 存储库路径中直接看到组 ID。
当然,你可以解析这些信息并以更结构化的方式打印出来,但我建议只是像这样打印它,让阅读日志的人脑进行解析。
就像我之前在评论中所说的那样,这在我向您展示的情况下效果很好,也适用于不同的项目,而不仅仅是在单个多模块项目中。在应用服务器部署或 uber JAR 情况下,您会看到什么样的信息,很大程度上取决于具体情况。没有单一的通用答案,我无法为您完成所有工作。我向您展示了几个选项,现在您可以select一个。
SOLUTION 到 post 主要描述中解释的场景,并考虑每个项目根目录中存在的 pom。
注意 - 请查看@kriegaex 建议以获得更好的方法。
添加maven-model依赖:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>3.3.1</version>
</dependency>
此依赖项具有有助于在运行时和跨项目获取所有 pom 相关信息的方法。
快速示例:
model.getArtifactId() -> 获取项目的工件 ID。
> import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.apache.log4j.Logger;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
public class MavenTest {
private static String FILE_NAME = "pom.xml";
private MavenXpp3Reader reader;
private Logger log = Logger.getLogger(MavenUtil.class.getName());
private Model model;
public MavenUtil() throws FileNotFoundException, IOException, XmlPullParserException {
this(FILE_NAME);
}
public MavenUtil(String absoluteFilePath) throws FileNotFoundException, IOException, XmlPullParserException {
log.info("[" + this.getClass().getSimpleName() + "]");
reader = new MavenXpp3Reader();
FileReader fr = null;
try {
fr = new FileReader(absoluteFilePath);
model = reader.read(fr);
} finally {
if (fr != null)
fr.close();
}
}
public String[] populateBuildInfo() {
String[] buildInfo = { model.getArtifactId().toUpperCase() + ":" + model.getVersion() };
return buildInfo;
}
public String getArtifactId(String absoluteFilePath) {
return model.getArtifactId();
}
}
在ProjectA中,我在ClassA
中有一个MethodA
,在不同的项目中添加了ProjectA的jar作为Maven依赖,不同的项目是调用 MethodA
.
要求是
每当 ClassA
的 MethodA
被任何其他项目调用时,我们需要记录被调用的项目工件 ID 和版本,考虑到这些项目中添加了 ProjectA 依赖项 pom.xml .
注意
以下仅适用于自身项目 (ProjectA),创建 属性 文件并打开 maven 资源过滤
Create a property file
src/main/resources/project.properties
with the below content
version=${project.version}
artifactId=${project.artifactId}
Now turn on maven resource filtering
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
方法A
public class ClassA {
final static Logger logger = Logger.getLogger(ClassA.class);
public void MethodA{
final Properties properties = new Properties();
properties.load(this.getClassLoader().getResourceAsStream("project.properties"));
logger.info(properties.getProperty("version"));
logger.info(properties.getProperty("artifactId"));
}
}
在项目 B 中调用 MethodA 时,我在记录器中得到以下输出
version=${project.version}
artifactId=${project.artifactId} which is incorrect.
预期输出:
version = 1.0.0
artifactId = ProjectB
有没有更好的方法来记录调用项目工件 ID?如果MethodA
被ProjectC调用,我们要获取ProjectC的artifactid和version。
要求:我们有30多个项目从ProjectA调用MethodA,所以我们不应该对调用项目做任何更改。
方案一:Maven资源过滤
如果您将 POM 片段放在正确的 POM 部分,您的 POM 片段应该可以正确替换变量:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
您可以在 target/classes
文件夹中查看结果。在我通过向您的方法名称添加一个空参数列表 ()
并将无意义的 this.getClassLoader()
替换为 getClass().getClassLoader()
来修复您的错误伪代码之后,代码甚至可以编译并执行一些有意义的操作。在 post 之前,您是否曾在 Whosebug 等 public 平台上测试过某些东西?
import java.io.IOException;
import java.util.Properties;
public class ClassA {
public void methodA() throws IOException {
final Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("project.properties"));
System.out.println(properties.getProperty("version"));
System.out.println(properties.getProperty("artifactId"));
}
public static void main(String[] args) throws IOException {
new ClassA().methodA();
}
}
运行在 mvn compile
之后从 IntelliJ IDEA 中 运行 时的控制台日志(因为我们需要 Maven 处理资源并将它们复制到 target/classes
):
1.9.8-SNAPSHOT
util
或者你的模块名称和版本是什么。
如果您看到的是变量名,要么是您的 classpath 没有指向 JAR,而是指向源目录他们中的一些人忘记了资源过滤。在 class 路径中首先找到的文件将被加载。所以在多模块项目中,最好使用不同的文件名,否则或多或少就是抽奖,先找到哪个。
下一个问题是让您的方面或其他模块知道要加载哪个资源文件,以便更好地链接到 class 或包名称,以便其他模块能够从包名猜测资源文件。然后,您确实需要在模块之间进行干净的包名称分隔。我真的很想知道这是否值得。
解决方案 B:模板化 Maven 插件 + package-info.java
+ 自定义注解
另一个想法是使用资源过滤或类似 org.codehaus.mojo:templating-maven-plugin
的插件将版本直接替换为 package annotation values in a package-info.java
file,然后在 运行 时间内从包信息中简单地获取值.我用那个插件做了一个快速而肮脏的本地测试,它运行良好。我建议暂时保持简单,只需解决您的资源过滤问题。如果您需要我刚才描述的更通用的解决方案,请告诉我。
项目结构
更新: 我将侵入我的一个项目的快速解决方案提取到一个新的 Maven 多模块项目中,以便向您展示一个干净的解决方案,如下所示:
比如说,我们有一个包含 3 个子模块的父 POM:
annotation
- 包含要在package-info.java
文件中的包上使用的注释。可以很容易地修改为也适用于 classes.library
- 应用程序模块要访问的示例库application
- 示例应用程序
您可以在 GitHub 上找到完整的项目: https://github.com/kriegaex/SO_Maven_ArtifactInfoRuntime_68321439
项目目录结构如下:
$ tree
.
├── annotation
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── annotation
│ └── MavenModuleInfo.java
├── application
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── de
│ │ │ └── scrum_master
│ │ │ └── Whosebug
│ │ │ └── q68321439
│ │ │ └── application
│ │ │ └── Application.java
│ │ └── java-templates
│ │ └── de
│ │ └── scrum_master
│ │ └── Whosebug
│ │ └── q68321439
│ │ └── application
│ │ └── package-info.java
│ └── test
│ └── java
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── application
│ └── ModuleInfoTest.java
├── library
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── de
│ │ └── scrum_master
│ │ └── Whosebug
│ │ └── q68321439
│ │ └── library
│ │ └── LibraryClass.java
│ └── java-templates
│ └── de
│ └── scrum_master
│ └── Whosebug
│ └── q68321439
│ └── library
│ └── package-info.java
└── pom.xml
请注意库和应用程序模块中的 src/java-templates
目录,其中包含 package-info.java
个文件。目录名称是 Templating Maven Plugin 的默认名称,使插件配置不那么冗长。
父 POM
<?xml version="1.0" encoding="UTF-8"?>
<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>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<modules>
<module>annotation</module>
<module>library</module>
<module>application</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
模块annotation
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>annotation</artifactId>
</project>
package de.scrum_master.Whosebug.q68321439.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MavenModuleInfo {
String groupId();
String artifactId();
String version();
}
模块library
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>library</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
package de.scrum_master.Whosebug.q68321439.library;
public class LibraryClass {}
请注意,以下文件需要位于 library/src/main/java-templates/de/scrum_master/Whosebug/q68321439/library/package-info.java
中。在这里你可以看到我们如何使用 Maven 属性在构建过程中通过 Templating Maven Plugin 将其替换为相应的值:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.Whosebug.q68321439.library;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
模块application
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>application</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.Whosebug.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package de.scrum_master.Whosebug.q68321439.application;
public class Application {
public static void main(String[] args) {}
}
请注意,以下文件需要位于 application/src/main/java-templates/de/scrum_master/Whosebug/q68321439/application/package-info.java
中。在这里你可以看到我们如何使用 Maven 属性在构建过程中通过 Templating Maven Plugin 将其替换为相应的值:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.Whosebug.q68321439.application;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
package de.scrum_master.Whosebug.q68321439.application;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
import de.scrum_master.Whosebug.q68321439.library.LibraryClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ModuleInfoTest {
@Test
public void test() {
String groupId = "de.scrum-master.Whosebug.q68321439";
MavenModuleInfo libMavenInfo = logAndGetMavenModuleInfo("Library Maven info", LibraryClass.class.getPackage());
assertEquals(groupId, libMavenInfo.groupId());
assertEquals("library", libMavenInfo.artifactId());
MavenModuleInfo appMavenInfo = logAndGetMavenModuleInfo("Application Maven info", Application.class.getPackage());
assertEquals(groupId, appMavenInfo.groupId());
assertEquals("application", appMavenInfo.artifactId());
}
private MavenModuleInfo logAndGetMavenModuleInfo(String message, Package aPackage) {
MavenModuleInfo moduleInfo = aPackage.getAnnotation(MavenModuleInfo.class);
System.out.println(message);
System.out.println(" " + moduleInfo.groupId());
System.out.println(" " + moduleInfo.artifactId());
System.out.println(" " + moduleInfo.version());
return moduleInfo;
}
}
运行 Maven 构建
现在 运行 Maven 构建通过 mvn clean test
:
(...)
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ application ---
[INFO] Surefire report directory: C:\Users\alexa\Documents\java-src\SO_Maven_ArtifactInfoRuntime_68321439\application\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running de.scrum_master.Whosebug.q68321439.application.ModuleInfoTest
Library Maven info
de.scrum-master.Whosebug.q68321439
library
1.0-SNAPSHOT
Application Maven info
de.scrum-master.Whosebug.q68321439
application
1.0-SNAPSHOT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec
(...)
识别来电者
假设所有的调用模块都实现了相同的scheme package info + special annotation,你可以这样打印调用者信息:
package de.scrum_master.Whosebug.q68321439.library;
import de.scrum_master.Whosebug.q68321439.annotation.MavenModuleInfo;
public class LibraryClass {
public void doSomething() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void doSomethingJava9() {
Class<?> callerClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
}
虽然 doSomething()
也适用于旧的 Java 版本(在 Java 8 上测试),但在 Java 9+ 上你可以使用 JEP 259 Stack-Walking API 作为doSomethingJava9()
中所示。在这种情况下,您不需要手动解析异常堆栈跟踪和处理异常。
解决方案 C:通过 URL classloader
识别调用 JAR假设您使用我的示例项目并从应用程序模块调用库(就像在上一节中一样),打印 JAR 信息的一种快速而肮脏的方法是这样的:
将此方法添加到 LibraryClass
:
public void doSomethingClassLoader() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
// Cheap way of getting Maven artifact name - TODO: parse
System.out.println(
callerClass
.getClassLoader()
.getResource(callerStackTraceElement.getClassName().replaceAll("[.]", "/") + ".class")
);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
同样,在 Java 9+ 上,您可以使用 Stack-Walking API 使代码更好,见上文。
调用方法 Application
:
public class Application {
public static void main(String[] args) {
// new LibraryClass().doSomething();
// new LibraryClass().doSomethingJava9();
new LibraryClass().doSomethingClassLoader();
}
}
现在从命令行和 运行 使用 3 个不同的 class 路径构建 Maven 应用程序,指向
target/classes
目录target
目录中的 JAR- 本地 Maven 存储库中的 JAR 为了查看打印到控制台的信息类型:
$ mvn install
(...)
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\classes" de.scrum_master.Whosebug.q68321439.application.Application
file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/classes/de/scrum_master/Whosebug/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\application-1.0-SNAPSHOT.jar" de.scrum_master.Whosebug.q68321439.application.Application
jar:file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/application-1.0-SNAPSHOT.jar!/de/scrum_master/Whosebug/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;c:\Users\Alexa\.m2\repository\de\scrum-master\Whosebug\q68321439\application.0-SNAPSHOT\application-1.0-SNAPSHOT.jar" de.scrum_master.Whosebug.q68321439.application.Application
jar:file:/C:/Users/alexa/.m2/repository/de/scrum-master/Whosebug/q68321439/application/1.0-SNAPSHOT/application-1.0-SNAPSHOT.jar!/de/scrum_master/Whosebug/q68321439/application/Application.class
如你所见
- 在情况1中,您可以从项目路径间接推断出Maven工件,
- 在情况 2 中,您在 JAR 名称中看到工件 ID 和版本,在项目路径中间接看到组 ID,
- 在情况 3 中,您会在 JAR 名称中看到工件 ID 和版本,在 Maven 存储库路径中直接看到组 ID。
当然,你可以解析这些信息并以更结构化的方式打印出来,但我建议只是像这样打印它,让阅读日志的人脑进行解析。
就像我之前在评论中所说的那样,这在我向您展示的情况下效果很好,也适用于不同的项目,而不仅仅是在单个多模块项目中。在应用服务器部署或 uber JAR 情况下,您会看到什么样的信息,很大程度上取决于具体情况。没有单一的通用答案,我无法为您完成所有工作。我向您展示了几个选项,现在您可以select一个。
SOLUTION 到 post 主要描述中解释的场景,并考虑每个项目根目录中存在的 pom。
注意 - 请查看@kriegaex 建议以获得更好的方法。
添加maven-model依赖:
<dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-model</artifactId> <version>3.3.1</version> </dependency>
此依赖项具有有助于在运行时和跨项目获取所有 pom 相关信息的方法。
快速示例:
model.getArtifactId() -> 获取项目的工件 ID。
> import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.apache.log4j.Logger;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
public class MavenTest {
private static String FILE_NAME = "pom.xml";
private MavenXpp3Reader reader;
private Logger log = Logger.getLogger(MavenUtil.class.getName());
private Model model;
public MavenUtil() throws FileNotFoundException, IOException, XmlPullParserException {
this(FILE_NAME);
}
public MavenUtil(String absoluteFilePath) throws FileNotFoundException, IOException, XmlPullParserException {
log.info("[" + this.getClass().getSimpleName() + "]");
reader = new MavenXpp3Reader();
FileReader fr = null;
try {
fr = new FileReader(absoluteFilePath);
model = reader.read(fr);
} finally {
if (fr != null)
fr.close();
}
}
public String[] populateBuildInfo() {
String[] buildInfo = { model.getArtifactId().toUpperCase() + ":" + model.getVersion() };
return buildInfo;
}
public String getArtifactId(String absoluteFilePath) {
return model.getArtifactId();
}
}