本文的主要目的是呈现maven在实际项目中的通常用法,包括
1.使用maven的聚合特性
2.使用maven的继承特性
3.将maven构件deploy至私服,如nexus
说明:为了演示聚合和继承特性,项目目录树如下
project-parent
|---project-a
|---src
|---pom.xml
|---project-b
|---src
|---pom.xml
|---pom.xml
一、project-parent目录中的pom.xml配置如下
<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>cn.cjc.maven.project</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 聚合关系配置 -->
<modules>
<!-- 这里用的是父子关系,还有一种是平行关系 -->
<module>project-a</module>
<module>project-b</module>
</modules>
<!-- 定义公共属性 -->
<properties>
<springframework.version>3.1.2.RELEASE</springframework.version>
<cxf.version>2.7.10</cxf.version>
<junit.version>4.8.2</junit.version>
</properties>
<!-- 依赖管理配置 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope><!-- scope也会被继承 -->
</dependency>
</dependencies>
</dependencyManagement>
<!-- 插件管理 -->
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<!-- <id>attach-sources</id> <phase>package</phase> -->
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- 部署管理配置 -->
<distributionManagement>
<repository>
<id>nexus</id>
<url>http://localhost:8081/nexus/content/repositories/custom-hr-1/</url>
</repository>
<snapshotRepository>
<id>nexus</id>
<url>http://localhost:8081/nexus/content/repositories/custom-hr-2</url>
</snapshotRepository>
</distributionManagement>
</project>
注意上面的<packaging>元素打包方式为pom,父项目的打包方式只能是pom。聚合关系配置里面的<module>元素的值是子项目的相对路径,子项目相对谁的路径呢?相对的是父项目中pom.xml,因为此处的两个子项目和父项目的pom.xml同在一个文件夹下,所以直接<module>子项目</module>即可。有时候可能会看到<module>../子项目</module>这样的写法,那是因为子项目不在父项目的目录下,所有的子项目和父项目的目录在物理上是一种平行关系,不过这个一般不常见。
有人要问,为什么需要聚合呢?就拿本文说,有两个子项目project-a和project-b,如果要打包成构件传到私服的话,需要分别进入两个子项目的目录,然后多次运行mvn deploy一个个地打包项目构件,而有了聚合后,只需要将所有子项目作为project-parent的聚合模块,然后进入父项目的目录,运行一次mvn deploy即可生成全部子项目的构件(同时也会生成父项目的构件,属于附属产物,没啥作用)。
上面的依赖管理配置的<dependencyManagement>元素是可被子项目继承的,但是为什么要用这个元素,而不直接用<dependencies>呢?原因在于父项目其实是不需要依赖任何jar包的,如果用了<dependencies>,那么会让父项目依赖了多余的jar包。而<dependencyManagement>不会让父项目依赖多余的jar包,同时也能让子项目继承,其下面的多个<dependencies>对子项目起到了一个约束作用,比如已经此处声明了spring-web的版本,那么子项目在引用spring-web的时候就不需要配置版本号了。
插件管理配置也一样,使用了<pluginManagement>元素,子项目要想使用父项目中声明的插件,只要配置一个插件artifactId即可,此插件的目标和行为都会直接从父项目的声明中继承,子项目中无需重复配置。
部署管理配置<distributionManagement>元素也是可被继承的,它定义了所有子项目构件要打包上传的私服地址。我使用的是nexus私服,且仓库类型Repository Type必须都是宿主型hosted才能打包传上去。此处我将发布版仓库和快照版仓库的id定义成了一样,也是为了图个方便,目的是在maven的settings.xml文件中只要为一个仓库id(nexus)配置用户名和密码即可。当然,发布版仓库和快照版仓库的id可以不一样,那样的话就要在settings.xml文件中分别对应两个仓库id来配置用户名和密码了。
二、project-a子项目中的pom.xml文件配置如下
<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>
<parent>
<groupId>cn.cjc.maven.project</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- maven的relativePath默认值即../pom.xml,故可省略 -->
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>project-a</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
</project>
看到<parent>元素节点 了没,不错,引用的就是project-parent作为父项目,其下的<relativePath>的值就是父项目中的pom.xml文件相对路径,相对谁呢?当然是父项目中的pom.xml相对子项目中pom.xml文件的位置,上面说可以去了这个<relativePath>配置,原因注释都有说。
子项目只定义了一个artifactId,其实它的groupId和version都将继承父项目的。神奇的地方出现了,子项目要想使用插件,而这个插件刚好在父项目中有声明过,那么子项目只要配置这个插件的artifactId即可,插件的目标和任务行为都将继承父项目中的配置。同样,子项目使用依赖jar包时,不需要配置版本号的,将会继承父项目中定义的版本号。
三、project-b子项目中的pom.xml文件配置如下
<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>
<parent>
<groupId>cn.cjc.maven.project</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- maven的relativePath默认值即../pom.xml,故可省略 -->
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>project-b</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
也没什么好说的,和project-a的pom.xml文件差不多,不过只强调一点,这个子项目依赖了junit这个jar包,因此这个jar包的版本号和依赖范围scope都将继承父项目中的版本号和依赖范围(test)。
四、maven的${user.home}\.m2\settings.xml文件配置如下
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:/Program Files/apache-maven-3.2.3/repository</localRepository>
<servers>
<!-- 部署构件到私服上时,需要配置私服上相应Release仓库或者Snapshot仓库的用户名和密码
这里的id要和项目的POM文件中的仓库id一致 -->
<server>
<id>nexus</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
<mirrors>
<!-- 用私服中的仓库组作为镜像仓库,项目中所有对远程仓库的下载请求都将被转到镜像仓库 -->
<mirror>
<id>my-mirror</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/nexus/content/groups/custom-gr-1/</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>my-profile</id>
<repositories>
<!-- 覆盖超级POM文件中的中央仓库配置 -->
<repository>
<id>central</id>
<!-- 由于配置了镜像仓库,所以此处的url已无作用 -->
<url>http://central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<!-- 覆盖超级POM文件中的中央仓库配置 -->
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>my-profile</activeProfile>
</activeProfiles>
</settings>
这个配置文件有必要好好说说,<servers>节点下只有一个server,其id与project-parent中配置的发布版和快照版仓库的id必须一致(这下知道为什么我要把两个仓库的id搞成一样的了吧。。。),只有配置了此项,子项目构件才能被发布到私服上。
看到<mirrors>节点了没?它下面有一个mirror,是镜像的意思,为什么要配置这个镜像呢?我不是自己搞了个nexus私服么,但是maven超级POM文件中配置的仓库地址是远程中央仓库的地址(这下知道了为什么不用在项目中配置仓库地址就能从远程仓库下载依赖包了吧?没错,所有的maven项目默认都继承这个super POM),我配置了这个镜像就把所有对别的仓库的下载请求转到我自己的私服了,因为我的<mirrorOf>的值是(*),表示所有仓库。镜像的url是我的nexus私服中一个组类型group仓库地址(这个组类型仓库中共有3个仓库,包含了project-parent里面配置的两个发布版和快照版的宿主型仓库,第三个仓库是maven代理仓库,也就是proxy类型的仓库)。
最后的最后,进入project-parent文件夹,在cmd命令行中熟练地运行mvn clean deploy命令,就会同时生成project-parent、project-a、project-b三个项目的构件,并自动上传至nexus私服,好了,一篇文章就这么愉快地搞定了。