1

我正在开发一个带有三个 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 来依赖两者,但是如何呢?通常当我最终陷入这种情况时,我的脑海中就会出现警报,而且我解决问题的方式通常有问题。但我似乎无法在这里找到另一种方法,它不包括其他“更大”的缺点:( 有人想吗?

4

3 回答 3

3

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

最初我有 3 件文物:

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

最后我又介绍了一件文物:

  • com.mycompany.app:app 依赖

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

  • org.apache.maven.plugins:maven 依赖插件

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

在那之后,三重奏很简单。我所要做的就是向工件添加一个资源,因为它不包含任何其他来源,它成为一个新的可靠工件。好的!

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

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

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

依赖 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人!

于 2012-09-19T01:19:42.777 回答
1

您可以使用Maven 依赖插件的复制目标将这些工件复制到项目中的目录中。复制同一工件的多个版本应该没问题。尽管它还不能帮助针对这些不同版本编译代码(您可能需要将附加的类路径配置为手写编译器参数)。

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

于 2012-09-18T07:54:49.317 回答
1

这个问题比 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 应用程序中为每个模型版本设置自己的单独的类加载器。不过我不知道我是否会推荐。)

于 2012-09-17T14:41:48.283 回答