小强说Maven(一)


写作原由

  说起Maven,它不是一个新的东西,大家多少都了解一些。之前为了了解的更多一些,我看了一本中文的Maven介绍书籍,但是那时一方面是经验尚浅,对一些概念的理解不深,一方面是书写的思路不是很清晰,后续没有相关总结,现在说不上一二。近期对Maven重新找了一本比较好的外文书籍,初读感觉很清爽,打算做一下读书笔记。(这里不是崇洋媚外,^_^)所以这里的原创其实是抱着了巨人的大腿。计划写八、九篇,大约一二周出一篇。
  第一讲,围绕Maven的核心要素POM(Project Object Model)。这一讲是纲领,也许有些介绍看起来是粗粒度的,但是浓缩的都是精华,抽取提炼,更容易看出Maven工程的骨干。


Maven的安装(略)

  这个部分本应该包含Maven的下载安装、本地仓库的配置、远程代理的设置、IDE的配置,但是相关资料很容易找到,这里不想多说了,百度一下,很容易的。



POM文件


  pom.xml是Maven工程的描述文件,就像一般的JavaEE web工程总有一个web.xml。   一般的pom文件包含以下内容:
<project>
    <!-- 本项目切身描述信息 -->
    <parent>...</parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <packaging>...</packaging>
    <name>...</name>
    <description>...</description>
    <url>...</url>
    <inceptionYear>...</inceptionYear>
    <licenses>...</licenses>
    <organization>...</organization>
    <developers>...</developers>
    <contributors>...</contributors>
    <!-- 依赖信息 -->
    <dependencies>...</dependencies>
    <dependencyManagement>...</dependencyManagement>
    <modules>...</modules>
    <properties>...</properties>
    <!-- 构建信息 -->
    <build>...</build>
    <reporting>...</reporting>
    
    <issueManagement>...</issueManagement>
    <ciManagement>...</ciManagement>
    <mailingLists>...</mailingLists>
    <scm>...</scm>
    <prerequisites>...</prerequisites>
    <!-- 仓库配置信息 -->
    <repositories>...</repositories>
    <pluginRepositories>...</pluginRepositories>
    
    <distributionManagement>...</distributionManagement>
    
    <profiles>...</profiles>
</project>

  这里只给出了一级的标签,这些就是龙骨。对于其中的标签后续会展开。这些标签最好是记住,根据代码中的分块,很容易记忆的。



POM文件的分类

  • 超级pom
  • 父pom
  • 子pom

  继承是面向对象语言的一大核心,妥善的处理可以减少代码的冗余和重复。Maven底层是用Java写的,自然不能缺少这一特性。子pom的配置内容可以继承父pom文件的相关配置。子pom文件的parent标签指明父pom文件的位置。parent的具体内容如下:

<parent>
    <groupId>org.wso2.carbon</groupId>
    <artifactId>platform-parent</artifactId>
    <version>4.2.0</version>
    <relativePath>../parent/pom.xml</relativePath>
</parent>

  其中relativePath是父pom.xml相对于本工程模块的相对路径。这个默认值是…/pom.xml。举例来说,如果是下面展示的路径,模块B使用A的作为父pom,就可以省略relativePath配置。

├── Module_A
│├── Module_B
│└── pom.xml
└── pom.xml

  当parent标签省略时,项目默认继承超级pom文件。这个就像是Java的默认继承Object类。超级pom文件在MAVEN_HOME/lib/maven-model-builder-3.2.3.jar - org/apache/maven/model/pom-4.0.0.xml。现在你应该明白一级标签modelVersion的含义了。



POM文件的继承和重写

使用以下命令可以查看当前pom文件继承父文件后的所有属性。

$ mvn help:effective-pom

  如果需要重写(Overide)父文件的某一项配置,可以在子文件中个性化对应的配置。但是注意相应的标签级次要完整,必要的标签不可少,如下面的artifactId,这个就是下面说的Maven坐标。

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-release-plugin</artifactId>
                <version>2.5</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>


POM文件的组件坐标(标识)

<groupId>com.packt</groupId>
<artifactId>sample-one</artifactId>
<version>1.0.0</version>

  上面的配置,多数人知道分别代表公司或组织的域名、工程名称、版本号。但是你知道Maven是怎么定位对应的jar文件(或war)的吗?Maven会在本地仓库的USER_HOME/.m2/repository/com/packt/sample-one/1.0.0/ 下面寻找对应的文件。

  当然,对于插件的定位,不需要groupId,具体的原因后续插件的专讲中会提到。



几个简单的例子


  这部分用几个简单的例子,来说明pom文件继承的妙用。

举例一、继承不如组合

  Java单继承的缘故,有时候继承不如组合更灵活。
  对于大型项目的依赖版本的管理,一种做法是通过父pom文件的dependencyManagement统一配置。即如下图所示,父pom的版本属性由配置约定,子pom继承时,无需配置版本,但是依然可以重写父类的版本。

<!-- 父pom -->
<dependencyManagement> 
  <dependencies> 
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-transport-mail</artifactId>  
      <version>${axis2-transports.version}</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.ws.commons.axiom.wso2</groupId>  
      <artifactId>axiom</artifactId>  
      <version>${axiom.wso2.version}</version> 
    </dependency> 
  </dependencies> 
</dependencyManagement>

<properties>
  <axis2.wso2.version>1.6.1.wso2v10</axis2.wso2.version>
</properties>
<!-- 子pom -->
<dependencies> 
  <dependency> 
    <groupId>org.apache.axis2.wso2</groupId>  
    <artifactId>axis2</artifactId> 
  </dependency>  
  <dependency> 
    <groupId>org.apache.ws.commons.axiom.wso2</groupId>  
    <artifactId>axiom</artifactId> 
  </dependency> 
</dependencies>

  但组合的处理是这样的,把共有的几个依赖抽取出来,单独作为一个pom文件,注意下面的packaging属性是pom。用到依赖的pom文件就能简化:

<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>axis2-client</artifactId>  
  <version>1.0.0</version>  
  <packaging>pom</packaging>  
  <dependencies> 
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-kernel</artifactId>  
      <version>1.6.2</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-adb</artifactId>  
      <version>1.6.2</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-transport-http</artifactId>  
      <version>1.6.2</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-transport-local</artifactId>  
      <version>1.6.2</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.axis2</groupId>  
      <artifactId>axis2-xmlbeans</artifactId>  
      <version>1.6.2</version> 
    </dependency> 
  </dependencies> 
</project>
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>my-axis2-client</artifactId>  
  <version>1.0.0</version>  
  <dependencies> 
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>axis2-client</artifactId>  
      <version>1.0.0</version> 
    </dependency> 
  </dependencies> 
</project>


举例二、可选依赖

  假设这样的一个场景,我们需要基于两种OSGi运行时进行编程,因而我们对两种的各自APIs都做了调用。但是实际程序运行的时候,依据实际底层的OSGi,只有一类的APIs在被调用。简言之,运行时(runtime)有一个jar,但是编译的时候却需要两个jar。对此,加入optional可选依赖。

<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>osgi.client</artifactId>  
  <version>1.0.0</version>  
  <dependencies> 
    <dependency> 
      <groupId>org.eclipse.equinox</groupId>  
      <artifactId>osgi</artifactId>  
      <version>3.1.1</version>  
      <scope>compile</scope>  
      <optional>true</optional> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.phoenix</groupId>  
      <artifactId>phoenix-core</artifactId>  
      <version>3.0.0-incubating</version>  
      <scope>compile</scope>  
      <optional>true</optional> 
    </dependency> 
  </dependencies> 
</project>
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>my.osgi.client</artifactId>  
  <version>1.0.0</version>  
  <dependencies>
    <!-- 对实际使用的明确指定 -->
    <dependency> 
      <groupId>org.eclipse.equinox</groupId>  
      <artifactId>osgi</artifactId>  
      <version>3.1.1</version>  
      <scope>compile</scope> 
    </dependency>  
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>osgi.client</artifactId>  
      <version>1.0.0</version>  
      <scope>compile</scope> 
    </dependency> 
  </dependencies> 
</project>


举例三、排除依赖

  有两个项目分别是这样的:

<!-- 项目1 -->
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>jose</artifactId>  
  <version>1.0.0</version>  
  <dependencies> 
    <dependency> 
      <groupId>com.nimbusds</groupId>  
      <artifactId>nimbus-jose-jwt</artifactId>  
      <version>2.26</version> 
    </dependency>  
    <dependency> 
      <groupId>net.minidev</groupId>  
      <artifactId>json-smart</artifactId>  
      <version>1.0.9</version> 
    </dependency> 
  </dependencies> 
</project>
<!-- 项目2 -->
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>jose.ext</artifactId>  
  <version>1.0.0</version>  
  <dependencies> 
    <dependency> 
      <groupId>com.nimbusds</groupId>  
      <artifactId>nimbus-jose-jwt</artifactId>  
      <version>2.26</version> 
    </dependency>  
    <dependency> 
      <groupId>net.minidev</groupId>  
      <artifactId>json-smart</artifactId>  
      <version>1.1.1</version> 
    </dependency> 
  </dependencies> 
</project>

  虽然他们的依赖json-smart版本不同,但是在各自的项目里,他们各自为政,不会出现问题。但是如果有第三个项目同时引用以上的项目:

<!-- 项目3 -->
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>jose.war</artifactId>  
  <version>1.0.0</version>  
  <version>war</version>  
  <dependencies>
    <!-- 项目1 -->
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>jose</artifactId>  
      <version>1.0.0</version> 
    </dependency>
    <!-- 项目2 -->
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>jose.ext</artifactId>  
      <version>1.0.0</version> 
    </dependency> 
  </dependencies> 
</project>

  由于Maven的传递性,各依赖项目原始所需的jar包也会引入。最终会引入json-smart 1.1.1的jar。但是如果我们想使用1.0.9的jar,我们使用exclusions来配置排除掉传递进来的属性。

<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>jose.war</artifactId>  
  <version>1.0.0</version>  
  <version>war</version>  
  <dependencies> 
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>jose</artifactId>  
      <version>1.0.0</version> 
    </dependency>  
    <dependency> 
      <groupId>com.packt</groupId>  
      <artifactId>jose.ext</artifactId>  
      <version>1.0.0</version>  
      <exclusions> 
        <exclusion> 
          <groupId>net.minidev</groupId>  
          <artifactId>json-smart</artifactId> 
        </exclusion> 
      </exclusions> 
    </dependency> 
  </dependencies> 
</project>


术语说明

  对于artifact的翻译,本讲是“项目”,但是这个其实就是一个区分用的字符标识。准确的说是jar包(或war包等)的名称(groupId组成路径,artifactId和version组成jar文件名称)。当然,你翻译为工艺品也没毛病,愿每个项目都是我们开发的工艺品。

发布了33 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39722475/article/details/105374114