許暁斌_Maven実戦(三)-マルチモジュールプロジェクトのPOM再構築

9612 ワード

from: http://www.infoq.com/cn/news/2011/01/xxb-maven-3-pom-refactoring
このコラムの前の記事では、POMの再構築の増加または削除について、再構築の前提である継続的な統合、およびコンテンツの追加または削除によってPOMの可読性と構築の安定性を向上させる方法を含む簡単な実用的なPOM再構築テクニックについて議論しました.しかし、実際のプロジェクトでは、これらのテクニックはまだ十分ではありません.特に、実際のMavenプロジェクトは基本的にマルチモジュールであり、モジュール間の関係を考慮せずに単一のPOMを再構築するだけでは、無駄な重複をもたらします.本稿では,マルチモジュールベースのPOM再構成技術について論じる.
繰り返すか、繰り返すか
プログラマーは犬のような嗅覚を持っていなければならない.繰り返しという最もよく見られる悪い味を嗅ぐには、どんなコートを着ていても、発見されたら、容赦なく徹底的に乾かすべきだ.POMが製品コードではないからといって、ここで発酵を繰り返すことを許さないでください.例えば、このようなコードには繰り返しがあります.
<dependency>
  <groupId>org.springframework</groupId>
  <artifactid>spring-beans</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactid>spring-context</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactid>spring-core</artifactId>
  <version>2.5</version>
</dependency>

1つのプロジェクトで異なるバージョンのSpringFrameworkコンポーネントを使用しますか?答えは明らかにできない.したがって、ここでは2.5を3回繰り返し書く必要はなく、Mavenプロパティを使用して2.5を以下のように抽出します.
<properties>
  <spring.version>2.5</spring.version>
</properties>
<depencencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactid>spring-beans</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactid>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactid>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>
</depencencies>

現在2.5は1つの場所にしか現れず、コードは少し長いが重複して消え、後日依存バージョンをアップグレードする際には、1つの場所を修正するだけで、ある依存をアップグレードする漏れを避けることができる.
読者はすでにこの例をよく知っているかもしれませんが、私がここでもう一度くどくど言うのは、後ろに敷物を作るためです.マルチモジュールPOMの再構築の目的はこの例と同じで、重複を解消するためです.モジュールが多ければ多いほど、潜在的な重複が多くなり、再構築が必要になります.
マルチモジュール依存構成の重複除外
このような大きなプロジェクトを考えると、10以上のMavenモジュールがあり、これらのモジュールは分業が明確で、それぞれの役割を果たし、相互間の結合度が比較的小さいので、他人が自分に与える影響をあまり考えずに自分のモジュールの中で開発に専念することができます.(はい、これは理想的な状況であることを認めます)では、モジュールAを符号化し始めました.まず、JUnit、Log 4 jなどの一般的な依存を導入する必要があります.
  <dependency>
    <groupId>junit</groupId>
    <artifactid>junit</artifactId>
    <version>4.8.2</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactid>log4j</artifactId>
    <version>1.2.16</version>
  </dependency>

私の同僚はモジュールBを開発しています.彼もJUnitとLog 4 jを使います(私たちは会議で議論しました.統一ユニットのテストフレームワークはTestNGではなくJUnitで、統一ログはJULではなくLog 4 jに実現しました.なぜこの決定をしたのか説明しません.とにかくそうしました).同僚は次の依存構成を書きました.
  <dependency>
    <groupId>junit</groupId>
    <artifactid>junit</artifactId>
    <version>3.8.2</version>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactid>log4j</artifactId>
    <version>1.2.9</version>
  </dependency>

何か問題が見えますか.はい、彼はJUnitが依存しているscopeを漏らしました.それは彼がMavenに慣れていないからです.他に何か問題がありますか.はい、バージョン!彼は私と同じようにJUnitとLog 4 jに頼っているが、バージョンが一致していないだろう.具体的にどのバージョンを使用するかは議論されていませんが、1つのプロジェクトがクラスライブラリの複数のバージョンに依存している場合は、非常に危険です.OK、今は2つのモジュールの2つの依存だけで、手動で修復しても問題はありませんが、10のモジュールであれば、1つのモジュールに10の依存以上はありますか?これは本当に泥沼のようで、いったん陥ると片付けにくい.
幸いなことに、Mavenは優雅な解決策を提供し、継承メカニズムとdependencyManagement要素を使用してこの問題を解決することができます.ただし、dependencyManangetではなくdependencyManangetです.親モジュールにdependenciesを構成することを考えているかもしれませんが、すべてのサブモジュールが自動的に継承され、依存が一致する目的を達成するだけでなく、大きなコードも省けますが、モジュールCの依存spring-aopを親モジュールに抽出するなど、問題がありますが、モジュールAとBはspring-aopを必要としませんが、直接継承します.dependencyManagementではこのような問題はありません.dependencyManagementは既存の依存構成にのみ影響しますが、依存は導入されません.たとえば、親モジュールでは次のように構成できます.
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactid>junit</artifactId>
      <version>4.8.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactid>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
  </dependencies>
</dependencyManagement>

この構成では、サブモジュールに依存は導入されませんが、JUnitとLog 4 jを使用する必要があるサブモジュールがある場合は、依存構成を簡略化できます.
  <dependency>
    <groupId>junit</groupId>
    <artifactid>junit</artifactId>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactid>log4j</artifactId>
  </dependency>

groupIdとartifactIdだけが必要になり、versionやscopeのような他の要素は、親POMのdependencyManagementを継承することで得られ、exclusionsの構成に依存している場合は、節約されたコードがより大きくなります.しかし、ここではなく、現在すべてのモジュールで使用されているJUnitとLog 4 j依存構成が一致していることを保証できることに重点を置いている.サブモジュールは、dependencyを構成しない場合、親モジュールのdependencyManagementのspring-aop依存は私に何の影響も与えません.
マルチモジュールMavenプロジェクトでは、依存性の一貫性を効果的に維持できるため、dependencyManagementはほとんど必要であることに気づいたかもしれません.
もともとdependencyManagementについて紹介したいのも悪くないと思いますが、数日前にサンンとの議論でもっと多くの内容を共有できました.それはdependencyManagementを使用する場合、親モジュールから継承するのではなく、特殊なimport scope依存を使用することができます.Sunngはそれを自分のMaven Recipe#0に列挙して、私は更に簡単に紹介します.
Mavenの継承はJavaの継承と同様に多重継承は実現できないことを知っています.10個、20個以上のモジュールが同じモジュールから継承されている場合、この親モジュールのdependencyManagementには多くの依存が含まれています.これらの依存をより明確に管理するために分類したい場合は、import scope依存はこの問題を解決することはできません.dependencyManagementを独立した依存管理用POMに配置し、依存を使用する必要があるモジュールでimport scope依存を使用するとdependencyManagementを導入できます.たとえば、依存管理のためのPOMを書くことができます.
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.juvenxu.sample</groupId>
  <artifactId>sample-dependency-infrastructure</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
  <dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactid>junit</artifactId>
          <version>4.8.2</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>log4j</groupId>
          <artifactid>log4j</artifactId>
          <version>1.2.16</version>
        </dependency>
    </dependencies>
  </dependencyManagement>
</project>

次に、この依存管理構成を継承しない方法で導入できます.
  <dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>com.juvenxu.sample</groupId>
          <artifactid>sample-dependency-infrastructure</artifactId>
          <version>1.0-SNAPSHOT</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
    </dependencies>
  </dependencyManagement>

  <dependency>
    <groupId>junit</groupId>
    <artifactid>junit</artifactId>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactid>log4j</artifactId>
  </dependency>

これにより、親モジュールのPOMは非常にきれいになり、専門のpackagingからpomのPOMに依存し、オブジェクト向け設計における単一の職責原則にも合致する.さらに、このような依存管理POMを複数作成し、依存をより細かく管理することもできます.このやり方は、継承ではなくオブジェクト向けのデザインで組み合わせを使うのと少し似たような味がします.
マルチモジュールプラグイン構成の重複除外
dependencyManagementと同様にpluginManagement要素管理プラグインも使用できます.一般的な使用法の1つは、プロジェクトのすべてのモジュールでMaven Compiler Plugginを使用する場合、Java 1.5を使用することと、JavaソースファイルをUTF-8にエンコードすることを指定することです.この場合、親モジュールのPOMでpluginmManagementを次のように構成できます.
<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

この構成は、すべてのサブモジュールのmaven-compiler-pluginに適用されます.Mavenにはmaven-compiler-pluginとライフサイクルのバインドが組み込まれているため、サブモジュールにはmaven-compiler-pluginの構成は必要ありません.
依存構成とは異なり、通常、すべてのプロジェクトはいずれの依存構成に対しても統一されるべきですが、プラグインはそうではありません.例えば、モジュールAがすべてのユニットテストを実行することを望むことができます.モジュールBはいくつかのテストをスキップします.この場合、maven-surefire-pluginを構成して実現する必要があります.そうすると、2つのモジュールのプラグイン構成は一致しません.つまり、単純にプラグイン構成を親POMのpluginManagementに抽出すると、すべての状況に適していないことが多いので、一般的なプラグイン構成だけがpluginManagementを使用して親POMに抽出されるべきであることに注意する必要があります.
プラグインプラグインプラグイン管理については、Mavenはimport scope依存と同様の管理を提供していないため、継承関係を利用するしかありませんが、一般的にプラグイン構成の数は依存構成ほど多くないため、問題ではありません.
小結
Maven POMの再構築については、ここで一段落します.基本的には、本編と前のMavenコラムで述べた再構築のテクニックを身につけ、その背後にある目的の原則を理解すれば、プロジェクトのPOMをより明確にしやすくし、潜在的なリスクを早期に回避することができます.Mavenはプロジェクトの構築と依存管理を支援するツールにすぎませんが、POMは正式な製品コードの一部ではありません.しかし、私たちもPOMに真剣に対処しなければならない.これはテストコードに似ている.以前はテストコードがあってもなくてもいいと思っていたかもしれないし、最適化テストコードを再構築することもできなかったが、敏捷な開発やTDDなどの方法がますます受け入れられるにつれて、テストコードは開発者の注目を集めている.そこでここでは、「使える」POMだけでなく、POMの悪い味を積極的に修復してほしいと思います.