【问题标题】:backwards compatible by artifact version按工件版本向后兼容
【发布时间】:2012-09-17 14:14:32
【问题描述】:

我正在开发一个带有三个 maven 项目的 RESTful webapp。这是我的 maven 设置(从一些细节中删除)

客户端(第二版):

<project>
    <groupId>com.mycompany.app</groupId>
    <artifactId>app-client</artifactId>
    <version>2.0</version>
    ...
    <dependency>
        <groupId>com.mycompany.app</groupId>
        <artifactId>app-model</artifactId>
        <version>2.0</version>
    </dependency>
</project>

模型(第二版):

<project>
    <groupId>com.mycompany.app</groupId>
    <artifactId>app-model</artifactId>
    <version>2.0</version>
</project>
...
<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <configuration>
        <generatePackage>com.mycompany.app.model.v2</generatePackage>
    </configuration>
</plugin>

Webapp(第二版):

<project>
    <groupId>com.mycompany.app</groupId>
    <artifactId>app-webapp</artifactId>
    <version>2.0</version>
    ...
    <dependency>
        <groupId>com.mycompany.app</groupId>
        <artifactId>app-model</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>com.mycompany.app</groupId>
        <artifactId>app-model</artifactId>
        <version>2.0</version>
    </dependency>
</project>

对于每个版本,我都会更新模型的包名以确保类名唯一。

com.mycompany.app:app-model:1.0 中的类 Foo

package com.mycompany.app.model.v1;

public class Foo {
    private String name;
}

com.mycompany.app:app-model:2.0 中的类 Foo

package com.mycompany.app.model.v2;

public class Foo {
    private String name;
    private int age;
}

在第一个版本中一切正常,因为 webapp 仅依赖于 com.mycompany.app:app-model:1.0。 在第二个版本中,maven 决定只依赖 com.mycompany.app:app-model:2.0。 这是正常行为,我理解为什么在正常情况下这是一件好事。 尽管如此。我的 Backwords 兼容性代码(在服务器中)想要使用 com.mycompany.app:app-model:1.0 中的类,因为在该版本中发布的客户端使用这些类。 新客户端(2.0 版)的代码希望使用 com.mycompany.app:app-model:2.0 类。

我确信有一种方法可以让 maven 依赖这两者,但是怎么做呢? 通常当我最终陷入这种情况时,我的脑海中就会出现警报,而且我解决问题的方式通常有问题。 但我似乎在这里找不到另一种方法,它不包括其他“更大”的缺点:( 有人想吗?

【问题讨论】:

    标签: java xml maven


    【解决方案1】:

    所以,为了回答我自己的问题,这就是我想出的。我认为这是非常严格的,而且不是开箱即用的。

    最初我有 3 个人工制品:

    • com.mycompany.app:app-client
    • com.mycompany.app:app-model
    • com.mycompany.app:app-webapp

    我最后又介绍了一件神器:

    • com.mycompany.app:app-depend

    此工件唯一的工作是将所有已发布的模型工件捆绑到一个可靠的工件中。为此,我遵循了 Esko 的建议并使用了 maven 插件:

    • org.apache.maven.plugins:maven-dependency-plugin

    目标是解包。这会将所有 .class 文件放在目标下的一个目录中,并且由于我确保每个模型版本都在一个独特的包中生成类,因此它运行良好,没有任何覆盖。我只包含了 .class 文件并省略了所有其他文件(MANIFEST.MF、pom.xml 和 pom.properties)

    在那之后,矩阵很简单。我所要做的就是向工件添加一个资源,并且由于它不包含任何其他来源,它成为一个新的可靠工件。不错!

    现在,为了让所有这些都按我想要的方式工作,我将新工件声明为我的 webapp 工件中的依赖项,让我可以访问模型的所有版本中的所有类。另一方面,客户端仍然只需要依赖模型,因为客户端只对最新的模型版本(发布时间)感兴趣。

    我还设法设计了 webapp,以便如果来自版本 1 的客户端调用 webapp,他会在模型版本 1 中得到响应,这是他理解的。同时,当来自版本 2 的客户端调用 webbap 时,他会在模型版本 2 中得到响应,这是他理解的。这是一直以来的目标,我认为这将促进版本控制 av my api。

    我应该提到,webapp 有它自己的域模型(独立但类似于表示模型),因此所有请求都会从域转换到表示。对于第 1 版,这两个模型几乎相同,但在第 2 版中,域发生了变化,(最新的)演示也发生了变化。 webapp 最好将域转换为表示模型 1 和 2,但模型 1 可能没有所有信息。现在,如果领域模型变化如此之大以至于尝试将其转换为表示模型 1 没有任何意义,我认为是时候停止支持该模型了...

    depend maven pom 最终看起来像这样(对于第 2 版)

    <project>
    
      <groupId>com.mycompany.app</groupId>
      <artifactId>app-depend</artifactId>
      <version>2.0</version>
      <packaging>jar</packaging>
    
      <build>
        <resources>
          <resource>
            <!-- this is where 'unpack' puts the files -->
            <directory>${project.build.directory}/dependency</directory>
            <filtering>false</filtering>
          </resource>
        </resources>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.5.1</version>
            <executions>
              <execution>
                <id>unpack</id>
                <!-- we must do the 'unpack' before the building the jar -->
                <phase>generate-sources</phase>
                <goals>
                  <goal>unpack</goal>
                </goals>
                <configuration>
                  <artifactItems>
                    <artifactItem>
                      <groupId>com.mycompany.app</groupId>
                      <artifactId>app-model</artifactId>
                      <version>1.0</version>
                      <type>jar</type>
                      <includes>com/mycompany/app/model/v1/**</includes>
                    </artifactItem>
                    <artifactItem>
                      <groupId>com.mycompany.app</groupId>
                      <artifactId>app-model</artifactId>
                      <version>2.0</version>
                      <type>jar</type>
                      <includes>com/mycompany/app/model/v2/**</includes>
                    </artifactItem>
                    <!-- future releases will go here -->
                  </artifactItems>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    
    </project>
    

    这就是 IT 伙计们!

    【讨论】:

      【解决方案2】:

      问题比 Maven 更深。默认情况下,JVM 只会加载一个类的一个版本。如果您的程序的类路径中有两个版本的com.mycompany.app.model.ModelObject(例如),当您的一个类请求ModelObject 时,类加载器将出去并加载它找到的第一个。

      我看到在公共 Web 服务中处理此问题的一种方法是根据所有类的版本为其命名。例如,版本 1 模型类可以放在包 com.mycompany.app.model_1 中,版本 2 模型类可以放在 com.mycompany.app.model_2 中。不过,在这种情况下,您(和客户)最终可能会编写相当多的额外代码。

      可能更简单的方法是为您想要支持的模型的每个版本托管一个单独的 webapp,webapp 路径基于模型版本。所以在这种情况下,版本 1 的 webapp 可能托管在 http://app.mycompany.com/webapp_1/... 和版本 2 的 http://app.mycompany.com/webapp_2/...。 (Web 服务器为每个 webapp 使用单独的类加载器,因此 webapp_1 和 webapp_2 可以使用同一类的不同版本。)

      (如果您喜欢冒险,也可以尝试在单个 web 应用程序中为每个模型版本设置自己的单独类加载器。不过我不知道是否会推荐。)

      【讨论】:

      • 感谢您的回答。我想我需要澄清一下。我的意思是“对于每个版本,我都会更新模型的包名称以确保唯一的类名”,这正是您所描述的。即 Foo 类将是 com.mycompany.app.model.v1.Foo 和 com.mycompany.app.model.v2.Foo 取决于版本,因此它们之间将是唯一的并且可以存在于相同的类路径中。还有什么想法吗?
      • 糟糕,对不起!我错过了那部分。 (脸红)我认为最简单的解决方案可能是为每个版本设置不同的工件 ID。这将有助于确保您的所有 Maven 插件都能正确解释依赖关系,并强调 v2 不是 v1 的直接替代品这一事实。
      • 是的,也许这就是要走的路。它肯定会让 maven 更快乐。但另一方面,Maven 只是我的构建工具,不应该决定我设计项目的方式。在我看来,version 只是处理 api 演变的正确概念。第三 com.mycompany.app:app-model-v1:1.0 对我来说似乎有点多余和困惑。尽管如此,以 maven 方式使用 maven 肯定是有一点意义的......在我做出任何决定之前,我会详细说明 @Esko 的建议,感谢您的意见。
      【解决方案3】:

      您可以使用Maven Dependency plugin's copy goal 将这些工件复制到您项目的目录中。复制同一工件的多个版本应该没问题。虽然它还不能帮助针对这些不同版本编译代码(您可能需要将额外的类路径配置为手写compiler arguments)。

      或者然后只是更改每个版本的工件 ID。

      【讨论】:

      • 好的。所以这将是“诡计多端”的解决方案,正如我所说,这让我觉得我可能会在这个设计上开局不好。无论如何,我还没有修改 maven,从您建议的内容开始,也尝试了一些其他插件(出现了一些旧的好东西,例如 assemble 插件),并提出了一个我认为可以的解决方案。在做这种事情时,我希望将其保持在最低限度,尤其是为了维护。易读易懂。 :)
      猜你喜欢
      • 2021-07-22
      • 1970-01-01
      • 1970-01-01
      • 2021-06-03
      • 1970-01-01
      • 1970-01-01
      • 2021-10-09
      • 2020-03-21
      • 1970-01-01
      相关资源
      最近更新 更多