簡體   English   中英

如何記錄 pom 工件和版本

[英]How to log the pom artifact and version

ProjectA 中,我在ClassA有一個MethodA ,在不同的項目中添加了 ProjectA jar 作為 Maven 依賴項,並且不同的項目正在調用MethodA

要求是

每當ClassA MethodA被任何其他項目調用時,我們都需要記錄被調用的項目工件 ID 和版本,考慮到這些項目 pom.xml 中添加了 ProjectA 依賴項。

筆記

以下僅適用於自項目(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,因此我們不應在調用項目中進行任何更改。

方案A:Maven資源過濾

如果你把它放在正確的 POM 部分,你的 POM 片段應該正確替換變量:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

您可以在target/classes文件夾中檢查結果。 在我通過向您的方法名稱添加一個空參數列表()並將無意義的this.getClassLoader()替換為getClass().getClassLoader()來修復您的錯誤偽代碼之后,該代碼甚至可以編譯並執行一些有意義的操作。 在向 StackOverflow 這樣的公共平台發布內容之前,您是否進行過測試?

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

或者無論您的模塊名稱和版本是什么。

如果您看到變量名,要么您的類路徑不指向 JAR,而是以某種方式指向源目錄,要么您有多個帶有project.properties文件的模塊,並且其中一個忘記了資源過濾。 無論哪個文件首先在類路徑上找到,都將被加載。 所以在一個多模塊的項目中,最好使用不同的文件名,否則如果先找到哪個就或多或少是個抽簽。

下一個問題是您的方面或其他模塊知道要加載哪個資源文件,以便更好地以某種方式鏈接到類或包名稱,以便其他模塊能夠從包名稱猜測資源文件。 那么您確實需要在模塊之間進行干凈的包名分離。 我真的想知道這是否值得麻煩。

方案B:模板化Maven Plugin + package-info.java + 自定義注解

另一個想法是使用資源過濾或像org.codehaus.mojo:templating-maven-plugin將版本直接替換 package-info.java文件中的包注釋值,然后在運行時從包信息。 我使用該插件進行了快速而骯臟的本地測試,效果很好。 我建議現在保持簡單,只解決你的資源過濾問題。 如果您需要我剛剛描述的更通用的解決方案,請告訴我。

項目結構

更新:我將侵入我的一個項目的快速解決方案提取到一個新的 Maven 多模塊項目中,以便向您展示一個干凈的解決方案,如下所示:

假設我們有一個包含 3 個子模塊的父 POM:

  • annotation - 包含要在package-info.java文件中的包上使用的注釋。 可以很容易地修改為也適用於類。
  • library - 應用程序模塊要訪問的示例庫
  • application - 示例應用程序

你可以在 GitHub 上找到完整的項目: https : //github.com/kriegaex/SO_Maven_ArtifactInfoRuntime_68321439

項目的目錄布局如下:

$ tree
.
├── annotation
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── de
│                   └── scrum_master
│                       └── stackoverflow
│                           └── q68321439
│                               └── annotation
│                                   └── MavenModuleInfo.java
├── application
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   │   └── de
│       │   │       └── scrum_master
│       │   │           └── stackoverflow
│       │   │               └── q68321439
│       │   │                   └── application
│       │   │                       └── Application.java
│       │   └── java-templates
│       │       └── de
│       │           └── scrum_master
│       │               └── stackoverflow
│       │                   └── q68321439
│       │                       └── application
│       │                           └── package-info.java
│       └── test
│           └── java
│               └── de
│                   └── scrum_master
│                       └── stackoverflow
│                           └── q68321439
│                               └── application
│                                   └── ModuleInfoTest.java
├── library
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── de
│           │       └── scrum_master
│           │           └── stackoverflow
│           │               └── q68321439
│           │                   └── library
│           │                       └── LibraryClass.java
│           └── java-templates
│               └── de
│                   └── scrum_master
│                       └── stackoverflow
│                           └── 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.stackoverflow.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.stackoverflow.q68321439</groupId>
    <artifactId>maven-artifact-info-runtime</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>annotation</artifactId>

</project>
package de.scrum_master.stackoverflow.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.stackoverflow.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.stackoverflow.q68321439</groupId>
      <artifactId>annotation</artifactId>
      <version>${project.version}</version>
    </dependency>
  </dependencies>

</project>
package de.scrum_master.stackoverflow.q68321439.library;

public class LibraryClass {}

請注意,以下文件需要位於library/src/main/java-templates/de/scrum_master/stackoverflow/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.stackoverflow.q68321439.library;

import de.scrum_master.stackoverflow.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.stackoverflow.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.stackoverflow.q68321439</groupId>
      <artifactId>annotation</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>de.scrum-master.stackoverflow.q68321439</groupId>
      <artifactId>library</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>de.scrum-master.stackoverflow.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.stackoverflow.q68321439.application;

public class Application {
  public static void main(String[] args) {}
}

請注意,以下文件需要位於application/src/main/java-templates/de/scrum_master/stackoverflow/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.stackoverflow.q68321439.application;

import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
package de.scrum_master.stackoverflow.q68321439.application;

import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
import de.scrum_master.stackoverflow.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.stackoverflow.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 構建

現在通過mvn clean test運行 Maven 構建:

(...)
[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.stackoverflow.q68321439.application.ModuleInfoTest
Library Maven info
  de.scrum-master.stackoverflow.q68321439
  library
  1.0-SNAPSHOT
Application Maven info
  de.scrum-master.stackoverflow.q68321439
  application
  1.0-SNAPSHOT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec
(...)

識別來電者

假設所有調用模塊都使用包信息+特殊注釋實現相同的方案,您可以像這樣打印調用者信息:

package de.scrum_master.stackoverflow.q68321439.library;

import de.scrum_master.stackoverflow.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 類加載器識別調用 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();
  }
}

現在從命令行構建 Maven 應用程序並使用 3 個不同的類路徑運行,指向

  1. target/classes目錄
  2. target目錄中的 JAR
  3. 本地 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.stackoverflow.q68321439.application.Application

file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/classes/de/scrum_master/stackoverflow/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.stackoverflow.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/stackoverflow/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\stackoverflow\q68321439\application\1.0-SNAPSHOT\application-1.0-SNAPSHOT.jar" de.scrum_master.stackoverflow.q68321439.application.Application

jar:file:/C:/Users/alexa/.m2/repository/de/scrum-master/stackoverflow/q68321439/application/1.0-SNAPSHOT/application-1.0-SNAPSHOT.jar!/de/scrum_master/stackoverflow/q68321439/application/Application.class

如你看到的

  • 在第一種情況下,您可以從項目路徑中間接推斷出 Maven 工件,
  • 在情況 2 中,您會在 JAR 名稱中看到工件 ID 和版本,在項目路徑中間接看到組 ID,
  • 在情況 3 中,您會直接在 Maven 存儲庫路徑中看到 JAR 名稱中的工件 ID 和版本以及組 ID。

當然,您可以解析該信息並以更結構化的方式打印它,但我建議簡單地像這樣打印它,讓閱讀日志的人腦進行解析。

就像我之前在評論中所說的那樣,這在我向您展示的情況下效果很好,也適用於不同的項目,而不僅僅是在單個多模塊項目中。 在應用服務器部署或 uber JAR 情況下,您會看到哪些類型的信息,很大程度上取決於具體情況。 沒有單一的、通用的答案,我不能為你完成你的全部工作。 我向您展示了幾個選項,現在您可以選擇一個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM