为什么解析的依赖关系在 Java 8 和 Java 11 之间不同?

Why does resolved dependencies differ between Java 8 and Java 11?

给定一个非常简单的 Maven 项目,其中包含一个包含单个依赖项的 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>org.example</groupId>
  <artifactId>maven-test</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.apache.cxf.xjc-utils</groupId>
      <artifactId>cxf-xjc-runtime</artifactId>
      <version>3.3.0</version>
    </dependency>
  </dependencies>

</project>

当 运行 mvn dependency:tree 根据 Java 版本给出不同的结果。

与Java8:

[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< org.example:maven-test >-----------------------
[INFO] Building maven-test 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ maven-test ---
[INFO] org.example:maven-test:jar:1.0-SNAPSHOT
[INFO] \- org.apache.cxf.xjc-utils:cxf-xjc-runtime:jar:3.3.0:compile
[INFO]    \- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.2:compile
[INFO]       \- jakarta.activation:jakarta.activation-api:jar:1.2.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.982 s
[INFO] Finished at: 2020-06-22T15:05:56+02:00
[INFO] ------------------------------------------------------------------------

与Java11:

[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< org.example:maven-test >-----------------------
[INFO] Building maven-test 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ maven-test ---
[INFO] org.example:maven-test:jar:1.0-SNAPSHOT
[INFO] \- org.apache.cxf.xjc-utils:cxf-xjc-runtime:jar:3.3.0:compile
[INFO]    +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.2:compile
[INFO]    |  \- jakarta.activation:jakarta.activation-api:jar:1.2.1:compile
[INFO]    +- javax.annotation:javax.annotation-api:jar:1.3.1:compile           <-- This and below only with Java 11
[INFO]    +- javax.xml.ws:jaxws-api:jar:2.3.0:compile
[INFO]    |  +- javax.xml.bind:jaxb-api:jar:2.3.0:compile
[INFO]    |  \- javax.xml.soap:javax.xml.soap-api:jar:1.4.0:compile
[INFO]    \- javax.activation:activation:jar:1.1.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.261 s
[INFO] Finished at: 2020-06-22T15:05:51+02:00
[INFO] ------------------------------------------------------------------------

我原以为这两个 Java 版本的树是一样的。

Maven 版本为 3.6.0.

为什么 Java 版本之间解析的依赖关系不同?

如果依赖关系已经存在于树的较高层,则依赖树 mojo 会修剪较低级别的依赖关系。

我们可以使用详细标志 (-Dverbose) 来显示排除的依赖项。

查找特定工件:mvn dependency:tree -Dverbose -Dincludes=[groupId]:[artifactId]:[type]:[version]

请访问Maven Dependency Tree了解更多。

Java版本依赖树不同的原因在依赖中找到:

<dependency>
  <groupId>org.apache.cxf.xjc-utils</groupId>
  <artifactId>cxf-xjc-runtime</artifactId>
  <version>3.3.0</version>
</dependency>

这又将 xjc-utils 作为其父级:

<parent>
    <groupId>org.apache.cxf.xjc-utils</groupId>
    <artifactId>xjc-utils</artifactId>
    <version>3.3.0</version>
</parent>

在这个 POM 文件中,我们找到了使用 Java 8:

构建时排除的依赖项
<profile>
    <id>java9-plus</id>
    <activation>
        <jdk>[9,)</jdk>
    </activation>
    <dependencies>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.ws</groupId>
            <artifactId>jaxws-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>
 </profile>

这本身就很重要,只有在使用 Java 9 或更高版本时才会包含依赖项,如以下范围所述:<jdk>[9,)</jdk>。标记的文档说明:

Specifies that this profile will be activated when a matching JDK is detected. For example, 1.4 only activates on JDKs versioned 1.4, while !1.4 matches any JDK that is not version 1.4.

此激活配置文件确保在使用 Java 11 时包含这些依赖项:

[INFO]    +- javax.annotation:javax.annotation-api:jar:1.3.1:compile
[INFO]    +- javax.xml.ws:jaxws-api:jar:2.3.0:compile
[INFO]    |  +- javax.xml.bind:jaxb-api:jar:2.3.0:compile
[INFO]    |  \- javax.xml.soap:javax.xml.soap-api:jar:1.4.0:compile
[INFO]    \- javax.activation:activation:jar:1.1.1:compile

有关激活的更多信息,请参阅 official Maven documentation:

Activations are the key of a profile. The power of a profile comes from its ability to modify the basic POM only under certain circumstances. Those circumstances are specified via an activation element.

连同另一个基于Java版本的激活示例:

<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
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <profiles>
    <profile>
      <id>test</id>
      <activation>
        <activeByDefault>false</activeByDefault>
        <jdk>1.5</jdk>
        <os>
          <name>Windows XP</name>
          <family>Windows</family>
          <arch>x86</arch>
          <version>5.1.2600</version>
        </os>
        <property>
          <name>sparrow-type</name>
          <value>African</value>
        </property>
        <file>
          <exists>${basedir}/file2.properties</exists>
          <missing>${basedir}/file1.properties</missing>
        </file>
      </activation>
      ...
    </profile>
  </profiles>
</project>