本文概述
- 前言
- Maven安装或更新
- Maven的生命周期、插件、目标和阶段
- Maven中的Archetype
- Maven资源过滤
- Maven多项目配置
- Maven单项目完整配置
- Maven JNI项目配置(Linux和Windows)
- 总结
前言
像之前分析Gradle构建配置一样,本文的目的旨在分析Gradle配置,并得到一个普遍的基本配置模板。构建工具几乎是所有项目开发都要用到的,而构建工具一般都带有一个配置文件,构建工具的最基本任务包括:
- 编译源文件文件。
- 处理资源文件
- 打包文件(字节码文件和资源文件)。
额外的任务包括:
- 清理构建生成文件。
- 项目依赖管理。
- 运行单元测试。
- 安装项目。
- 部署项目。
所以,构建工具的输入为一堆项目文件,输出为可运行文件及其相关资源文件。对于Maven也是一样的,Maven的任务由各种插件完成(Maven的插件由多个目标组成),而Gradle的任务由各种Task完成,对比来看,Gradle会比Maven配置更为自由一些。
Maven安装或更新
Maven的安装非常简单,简要流程如下:
- 从官网 HYPERLINK "https://maven.apache.org/download.html" https://maven.apache.org/download.html下载相关的安装包。
- 解压安装包到指定文件夹。
- 将Maven安装目录的bin目录配置到环境变量中。
- 在命令行中输入mvn -v查看maven版本信息,正常显示则表示安装成功。
Maven的安装也类似以上流程,但是这里有两个问题:仓库配置和镜像配置,首先进入Maven安装目录的conf目录中,打开settings.xml:
1、配置本地仓库:搜索localRepository,在下面添加配置:
<localRepository>E:\maven-repository</localRepository>
2、配置镜像地址:搜索mirrors,在其中添加镜像地址配置:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
如果是更新Maven,在使用新版Maven时,也要注意新版Maven是否跟随了旧版本的相关配置。
Maven的生命周期、插件、目标和阶段
如果我们要执行指定的构建任务,需要使用到Maven插件(plugin),Maven的插件由一系列的目标(goal)组成。在Maven中有构建生命周期的概念,构建的每个单元又称为阶段(phase),阶段实际上是映射到底层目标的(一个阶段可能对应有一个或多个目标)。执行某个构建阶段,可能会预先执行一些依赖的阶段(类似Gradle中的任务依赖),但是仅执行一个目标不会存在依赖的问。例如执行mvn compile,在执行compile编译阶段之前,会执行多个阶段,所有执行的阶段如下:
- validate
- generate-sources
- process-sources
- generate-resources
- process-resources
- compile
但是如果执行mvn jar:jar目标,则只会执行该目标,其它的都不会执行。
执行maven的命令一般形式为:mvn 阶段/目标,例如一个常见的清理阶段是mvn clean。
虽然maven官方说一个插件由不同的目标组成,但是似乎将插件看做一个任务集合更形象(配合Gradle),所以如果要完成某些特殊的任务,一个方法是使用第三方插件,或者自己开发插件,或者简单的任务由maven自带插件完成。
使用基于maven-archetype-quickstart模板生成的Maven项目一般带有一些默认的构建生命周期阶段,你可以打开在IDEA的右边面板,右键执行这些构建阶段。如果你展开“Plugins“,你可以看到Maven配置的所有插件,对每个插件逐个展开可以看到每个插件的不同构建目标(一个插件由多个目标组成),如下图:
由上图你还可以看到一些Maven目标的特殊记法,例如compiler:testCompile,前面是插件名称,后面是插件中的目标。Maven中的阶段和目标可以并列执行,例如:
mvn clean dependency:copy-dependencies package
clean和package是maven的构建阶段,而copy-dependencies是dependency中的一个目标。
综上总结如下:
- 一个构件生命周期lifecycle由不同的阶段phase构成,这些阶段有前后依赖的关系,执行一个阶段可能会预先执行一些依赖的阶段。
- 一个构建插件中可能会有一个或多个目标goal,不同或相同的目标会被映射的到一个阶段中,不被映射的目标仍然可用被单独执行,执行任意一个目标不会影响其它目标。
- 一个目标是一个单独的子功能,目标一般被映射到一个阶段中,阶段属于构建生命周期。
例如,下面的例子是将插件display-maven-plugin中的一个目标time映射到阶段process-test-resources中,这样当执行到process-test-resources阶段的时候,就会去执行time目标。
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>display-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>time</goal>
</goals>
</execution>
</executions>
</plugin>
Maven中的Archetype
在IDEA中创建一个Maven项目一般都可以选择一个Archetype,Archetype是原型的意思,Maven中的Archetype是一个项目模板工具包,用于生成特定配置的Maven项目。
例如maven-archetype-quickstart可以生成一个普通的Java Maven项目,maven-archetype-webapp用于生成一个Maven Web项目。
Archetype由maven的archetype插件支持,而archetype:generate只是其中的一个目标,用于生成指定格式的Java项目。
由此可见,Maven中的大部分工作都依赖于插件完成。
Maven资源过滤
Maven的资源过滤功能用于访问maven项目中定义的变量或属性,根据属性的来源分类,其使用方式分别如下:
- 来自POM:pom.xml是一个XML文件,maven将每个xml元素作为一个对象来处理。例如pom.xml的根元素是<project>,所以我们可以把project作为一个对象来使用,而${project.name}表示访问project元素中的name元素的值。
- 来自maven的配置文件settings.xml,该文件也是一个XML文件,使用方式类似pom.xml,例如${settings.localRepository}。
- 来自自定义filter文件src/main/filters/filter.properties:在这里定义的变量都可以使用,但是需要在pom.xml中配置:在<build>下添加filters配置。
- 来自pom.xml中<properties>定义的属性。
- 在资源文件中访问以上属性需要在pom.xml中配置:在<build>下添加resources配置,并将filtering设置为true。
- Java系统属性:Java系统属性或Java命令行-D参数指定的属性。
Maven多项目配置
假设项目app有两个模块:my-app和my-webapp,多项目配置如下:
首先在父项目app中添加子模块配置,module中的值为项目文件目录(有可能需要使用相对路径):
<modules>
<module>my-app</module>
<module>my-webapp</module>
</modules>
接着在每个子模块中添加父项目配置,有可能需要使用relativePath指定pom.xml文件:
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>app</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
子模块之间可以互相依赖,例如my-webapp依赖my-app的依赖配置如下:
...
<dependencies>
<dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
...
</dependencies>
Maven多项目继承和聚合配置方式如下:
- 在每个子POM中指定它们的父POM。
- 将父POM的打包类型设置为“pom”。
- 在父POM中指定其模块(子POM)的目录。
Maven单项目完整配置
下面是Maven单项目的一些基本配置,不算完整,但可以借用,或在此基础上添加,配置来源于github的配置:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.maven-samples</groupId>
<artifactId>single-module-project</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>A Single Maven Module</name>
<description>Sample single module Maven project with a working, deployable site.</description>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
</properties>
<distributionManagement>
<site>
<id>site-server</id>
<name>Test Project Site</name>
<url>file:///tmp/single-module-site</url>
</site>
</distributionManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.2.1</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.0</version>
<configuration>
<reportPlugins>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.11</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.5.1</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.3.3</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>taglist-maven-plugin</artifactId>
<version>2.4</version>
</plugin>
</reportPlugins>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.11</version>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.0.0.M1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<scm>
<connection>scm:git:git@github.com:gabrielf/maven-samples.git</connection>
<developerConnection>scm:git:git@github.com:gabrielf/maven-samples.git</developerConnection>
<tag>HEAD</tag>
<url>http://github.com/gabrielf/maven-samples</url>
</scm>
<prerequisites>
<maven>3.0.3</maven>
</prerequisites>
</project>
Maven JNI项目配置(Linux和Windows)
JNI项目的主要处理是:
- 根据native方法生成头文件,然后编写C/C++代码。
- 编译C/C++代码,这部分需要使用Linux和Windows对应的编译器,或结合相关的构建工具。
- 编译代码生成动态库或静态库。
- 库文件打包。
首先是项目结果,推荐的方式是在项目根目录创建clib,用于存放编译好的库文件。源码文件结果如下:
-src
--main/c
--main/java
Java编译插件配置,用于确保编译第一步进行:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
头文件生成配置,要注意的是,这里的文件名和类名不是动态的,你需要手动指定:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>native-maven-plugin</artifactId>
<executions>
<execution>
<id>javah</id>
<phase>generate-resources</phase>
<goals>
<goal>javah</goal>
</goals>
<configuration>
<!-- 头文件输出目录 ${basedir}为maven内置变量-->
<javahOutputDirectory>${basedir}/src/main/c</javahOutputDirectory>
<javahOutputFileName>learn_jni.h</javahOutputFileName>
<!-- 需要编译成头文件的class,必须是全限定类名 -->
<javahClassNames>
<javahClassName>learn.jni.Sample01</javahClassName>
</javahClassNames>
</configuration>
</execution>
</executions>
</plugin>
对于编译JNI代码,你应该使用对应平台的编译器进行编译,因为我们的JNI程序是在特定平台上运行的。如果编译成静态库,Linux下是.so,Windows下是.dll。
打包库文件,其配置如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-clib</id>
<phase>compile</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>clib</directory>
</resource>
</resources>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
对于最终项目打包,可以使用以下配置:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
其中,我们需要在项目根目录创建assembly.xml文件,内容如下:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
<format>dir</format>
</formats>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/classes</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>bin/start.sh</source>
<outputDirectory>/</outputDirectory>
<fileMode>0755</fileMode>
</file>
</files>
</assembly>
总结
好了,先到这里了。整体来说,相对于Gradle,Maven的配置没有那么复杂,主要是理解Maven的一些核心概念,例如依赖管理、插件、目标和生命周期等,最重要的则是插件,因为很多东西都可以使用插件来完成,本身也不难。
本文的单项目和多项目配置会放到github上,Gradle的完整配置也放到github,也不算完整吧,就是可以适配多数项目,以后遇到特殊的配置再逐渐添加进去,这样可以免去配置的很多麻烦,节省开发时间。