pom.xml文件(以下简称pom)可以说是maven项目的核心所在,项目的测试,打包,发布都离不开在pom中使用相关插件进行配置,下面的内容主要按照实现Maven中的某一个功能进行讲解。

安装 & 配置 Maven

在安装Maven之前,请先确保本机安装了Java,并且设置了JAVA_HOME。关于Java的安装,可以参考这里

Maven紫的安装不复杂,先去官网下载对应的压缩包并解压。然后设置环境变量。

Linux/ OSX

/etc/profile设置M2_HOME,记得用sudo权限

export M2_HOME=/{Maven解压后的目录}
export PATH=${M2_HOME}/bin:$PATH

如果用了sudo还是提示没有写入权限,可以将profile先拷贝一份出来,修改开拷贝的文件,然后再覆盖/etc下的

修改完之后记得source /etc/profile,之后执行mvn -v就可以看到设置成功了。

Windows

和JAVA_HOME的设置基本相同:

  1. 右键我的电脑选择属性,然后在左边栏点击高级系统设置

  2. 点击下方环境变量

  3. 现在下方的系统变量中新建,变量名为M2_HOME,然后浏览目录,选择刚才Maven解压后的目录

  4. 添加完成之后,在上方的用户变量里面找到Path,点击编辑

  5. %M2_HOME%\bin加入

  6. 新启动一个命令行输入mvn -v查看是否生效。

Maven安装完成之后,下面就正式开始讲解Maven的具体功能。

基本功能

项目结构

├── src/main/java          # 工程源码
├── src/main/resources     # 工程运行时所需的外部资源文件(数据集,图片)
├── src/test/java          # 测试源码
├── src/test/resources     # 测试代码所需的外部资源文件(数据集,图片)
├── LICENSE                # 
├── README.md              # 使用说明
└── pom.xml                # 项目配置文件

全局属性设置

在Maven里面经常对于一些配置信息(例如,版本号)需要在文件里面重复用到,后续如果需要修改,则需要在文件里面进行多出修改,十分的麻烦。为了解决这个问题,可以在Maven里面设置属性

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
</properties>

后续再用到1.8的时候,可以通过${maven.compiler.source}对其进行引用。这样,只要在上面修改了,下面的所有内容都会进行修改。

依赖引入

在开发项目的时候经常会用到外部的依赖,比如需要记录日志的地方就经常用到logback,编写一些单元测试需要用到mockito

在没有maven的帮助下,需要去各自的官网下载对应的jar包,然后再手动加入到classpath中,十分的麻烦。有了maven之后,只需要去maven仓库,搜索对应的依赖,然后把对应的标签加入到pom文件里面即可,十分的方便。

<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">
	<!-- Properties Management -->
	<properties>
		<logback.version>1.1.11</logback.version>
        <mockito.all.version>1.10.19</mockito.all.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mockito</groupId>
			<artifactId>mockito-all</artifactId>
			<version>${mockito.all.version}</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

上面看到mockito的依赖里面有<scope>test</scope>这样一行。表示mockito依赖只在测试阶段生效,在编译,打包阶段都不生效。具体含义下面细说。

常用命令

# 清理上次构建时自动生成的文件
$ mvn clean

# 清理 + 编译项目代码,生成可以在JVM运行的.class文件
$ mvn clean compile

# 清理 + 编译 + 跑src/test/java下所有@Test标签的测试
$ mvn clean test

# 清理 + 编译 + 测试 + 将.class打包成可发布的形式(原文:distributable format, such as a JAR)
$ mvn clean package

# 跳过上面的测试阶段
$ mvn clean package -Dmaven.test.skip=true

# 清理 + 编译 + 测试 + 打包 + 将本项目安装到本地仓库,这样其他Maven项目就可以在pom里面用<dependency/>进行依赖
$ mvn clean install

小结

看到这里,maven的基本用法已经学会了,就可以试着创建一个最简单的项目,在自己的IDE里面开发一个演示demo应该不是大问题了。下面会讲解maven提供的许多更高级的功能。

Maven概念理解

生命周期

maven在执行的时候有一个生命周期的概念。maven里面的一共有三种生命周期,defaultcleansite。下面如果没有特别说明,都是指default生命周期。

在一个生命周期里面会分为为若干个执行顺序固定的阶段。下面列出部分(完整的可以看这里),英文比较好懂,就不翻译了。

  • validate - validate the project is correct and all necessary information is available
  • compile - compile the source code of the project
  • test - test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
  • package - take the compiled code and package it in its distributable format, such as a JAR.
  • verify - run any checks on results of integration tests to ensure quality criteria are met
  • install - install the package into the local repository, for use as a dependency in other projects locally
  • deploy - done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.

例如,执行mvn clean package的时候,就涉及两个生命周期,显示clean的生命周期,然后是default生命周期。default生命周期里面依次执行了compile,testpackage(根据pom的配置不同,可能不止这三个,但这三个是肯定会按照这个顺序执行的)

父/子模块

以iotdb项目为例,可以看到我们的项目结构是

iotdb
 |-- tsfile
 |   `-- pom.xml
 `-- pom.xml

根目录中pom文件

<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.edu.tsinghua</groupId>
	<artifactId>root</artifactId>
	<version>0.8.0-SNAPSHOT</version>
	<packaging>pom</packaging>

	<name>IoTDB Root</name>
</project>

tsfile项目中pom文件

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <!--指定父项目-->
    <parent>
        <groupId>cn.edu.tsinghua</groupId>
        <artifactId>root</artifactId>
        <version>0.8.0-SNAPSHOT</version>
    </parent>
    <!--继承了父项目中的配置,groupId,version都不需要再指定了-->
    <artifactId>tsfile</artifactId>
</project>

当然,如果二者的相对路径发生变化,比如结构变成了这样:

iotdb
 |-- tsfile
 |   `-- pom.xml
 |-- root
 |   `-- pom.xml

tsfile项目中pom文件需要修改relativePath

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <!--指定父项目-->
    <parent>
        <groupId>cn.edu.tsinghua</groupId>
        <artifactId>root</artifactId>
        <version>0.8.0-SNAPSHOT</version>
        <relativePath>../root/pom.xml</relativePath>
    </parent>
    <!--继承了父项目中的配置,groupId,version都不需要再指定了-->
    <artifactId>tsfile</artifactId>
</project>

上述配置是让tsfile项目继承了root项目中的配置,但对于root项目而言,tsfile不是可见的。如果想让root知道自己有了这样一个子模块,需要在modules标签中定义子模块。这样就可以在根目录下操作各个子模块了。

<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.edu.tsinghua</groupId>
	<artifactId>root</artifactId>
	<version>0.8.0-SNAPSHOT</version>
	<packaging>pom</packaging>

	<name>IoTDB Root</name>
	<modules>
		<module>tsfile</module>
	</modules>
</project>

如果项目的结构是这样的,那么需要改写成<module>../tsfile</module>

iotdb
 |-- tsfile
 |   `-- pom.xml
 |-- root
 |   `-- pom.xml

最后再提一句,maven里面内置了许多变量可以使用,最常用的就是${project.basedir},可以表示当前项目所在的路径。

依赖

注意事项

从父项目到子项目,所有的依赖会转化为一张有向无环图,需要明确不能出现循环依赖

重复依赖的问题,假如有这样的依赖关系A -> B -> C -> D 2.0 和 A -> B -> D 1.0,那么D被依赖的两次,maven会选择从根节点道路最短的那个,这里的话就会用D 1.0。如果道路长度一样,就根据谁先定义用谁的规则。最好避免重复依赖的问题,可以用exclusions实现。

<project>
    <dependency>
        <groupId>group.a</groupId>
        <artifactId>artifact.c</artifactId>
        <version>1.0.0</version>
        <!--让C不依赖D-->
        <exclusions>
            <exclusion>
                <groupId>group.s</groupId>
                <artifactId>artifact.d</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</project>

依赖作用域

从上面的生命周期了解到,在构建的过程中涉及多个阶段,依赖作用域可以指定在哪些阶段依赖会生效,哪些不生效。

compile  -   默认作用域,编译,测试和运行阶段都生效,且具有传递性
provided -   This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. 
         -   For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. 
         -   This scope is only available on the compilation and test classpath, and is not transitive.
runtime  -   测试和运行阶段都生效,编译不生效
test     -   只在测试运行阶段生效
system   -   This scope is similar to provided except that you have to provide the JAR which contains it explicitly. 
         -   The artifact is always available and is not looked up in a repository.
import   -   This scope is only supported on a dependency of type pom in the <dependencyManagement> section. 
         -   It indicates the dependency to be replaced with the effective list of dependencies in the specified POM's <dependencyManagement> section. 
         -   Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency.

依赖组管理

假设项目结构是这样的,其中A和B都需要依赖项目D,但对于D要求的版本却各自不同。那么通常的做法是,在A项目里面的加入包含D的,指定对应的版本。在B项目里也加入对应的依赖,但是版本不同。

root
 |-- A
 |   `-- pom.xml
 |-- B
 |   `-- pom.xml
 |-- C
 |   `-- pom.xml

基本上在A和B的代码基本相同,只是版本不同,为了避免写重复代码,可以在root下使用进行统一定义和管理。

<!--pom.xml in root-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.9.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.httpcomponents</groupId>
                    <artifactId>httpclient</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.httpcomponents</groupId>
                    <artifactId>httpcore</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</dependencyManagement>

<!--pom.xml in project A-->
<dependencies>
    <dependency>
        <groupId>org.apache.thrift</groupId>
        <artifactId>libthrift</artifactId>
    </dependency>
</dependencies>

<!--pom.xml in project B-->
<dependencies>
    <dependency>
        <groupId>org.apache.thrift</groupId>
        <artifactId>libthrift</artifactId>
        <version>0.12.0</version>
    </dependency>
</dependencies>

从上面可以发现,A和B项目通过引用父项目的依赖,自己定义变得更加简洁了,要<exclusions>的内容也只需要在父项目写一次。另外,假如还有一个和A,B项目平级的C项目,虽然root里面定义了要依赖<libthrift>,但是C项目如果不像A项目里面显示地声明,就不会依赖。

最后还需要注意,子项目从父项目</dependencyManagement>找到自己要的<dependency>的时候,需要靠{groupId, artifactId, type, classifier}四元组,一般只会靠{groupId, artifactId}二元组,这个时候从<dependency>继承过来的参数值就会设置为默认值。type就有可能设为jar

<!--root in pom.xml-->
<project>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>group-c</groupId>
        <artifactId>artifact-b</artifactId>
        <version>1.0</version>
        <type>war</type>
        <scope>runtime</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

<!--Project A in pom.xml-->

<project>
  <dependencies>
    <dependency>
      <groupId>group-c</groupId>
      <artifactId>artifact-b</artifactId>
      <!--虽然父项目里面<dependencyManagement>已经声明了是war,但是这里还需要显示地再声明一次,不然就是<type>里面对应的就是默认值jar-->
      <type>war</type>
    </dependency>
  </dependencies>
</project>

官网原话是:NOTE: In two of these dependency references, we had to specify the element. This is because the minimal set of information for matching a dependency reference against a dependencyManagement section is actually {groupId, artifactId, type, classifier}. In many cases, these dependencies will refer to jar artifacts with no classifier. This allows us to shorthand the identity set to {groupId, artifactId}, since the default for the type field is jar, and the default classifier is null.

插件

最上面说到了maven的生命周期和它所包含的若干个阶段,在每一个阶段中都会执行一些默认的操作,比如在package阶段会把compile后的.class文件打包成jar文件。在这个时候后如果想要增加一些额外的操作,比如,把项目打的jar包默认在target下,要想让它放到自己指定的某个位置。

这个时候就需要用到maven提供的各种插件功能。

在maven中的插件分为两类,一类是Build plugins,在defaults生命周期里面起作用,在<build/>配置。另外一类是Reporting plugins,在site生命周期里面起作用,在<reporting/>配置。下面没有特别说明,都是指Build plugins。

对于一个plugin,它至少要有groupId, artifactId和version三个配置项。

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.1.1</version>
        <executions>
          <execution>
            <id>copy-jar-dependency</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <!-- configure the plugin here -->
              <outputDirectory>${project.basedir}/lib</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

然后就要说两个重要的概念goalphase

在一个maven插件里面,他有许多子功能,例如在maven-dependency-plugin里面有下面的这些功能,当你引入一个插件的时候,你需要指定自己要用到哪些子功能,也就是goal。然后在配置具体的信息。

dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared.
dependency:copy-dependencies takes the list of project direct dependencies and optionally transitive dependencies and copies them to a specified location, stripping the version if desired. This goal can also be run from the command line.
...
dependency:unpack like copy but unpacks.
dependency:unpack-dependencies like copy-dependencies but unpacks.

接下来是phase,这个表明<execution>里面的内容在哪个阶段会生效,<phase>package</phase>表明在package阶段生效,也就是说mvn package的时候这个插件会执行。

如果没有配置phase,那么就会设为默认值,如默认值果为空,那么在整个生命周期里面都不会执行。手动触发是:mvn dependency:copy-dependencies。maven插件通用的的触发方法是mvn xxx:{goal},xxx是在<artifactId>maven-xxx-plugin</artifactId>或者<artifactId>xxx-maven-plugin</artifactId>这里定义的。

最后再提醒一点就是<execution>里面有一个<id>,这个是用户自定义的,没有特别的含义,但是需要保证在<executions>里面是唯一的,但不要求各个插件之间唯一的。

插件组管理

用法和依赖组管理基本相同,这里不再赘述。

需要注意的是,dependenciesdependencyManagemen均是project下的直接子元素,但是pluginspluginManagement却是projectbuild的直接子元素。

profile

profile的出现主要是为了解决跨平台兼容性的问题。比如要用thrift生成代码,但是在windows和linux/macos上面生成的命令不相同,需要分情况来写。那么首先需要判断在那个环境里面,然后再执行对应的命令。profile相当于提供了switch(your condition) case1/case2…的功能。

<profiles>
  <profile>
    <activation>
      <jdk>[1.3,1.6)</jdk>
    </activation>
    ...
  </profile>
</profiles>

上面的例子就是检测系统jdk环境在[1.3,1.6)之间,然后出发自定义的操作。

下面举一个例子,根据不同操作系统来实现当前项目文件结构,在linux,osx上用的是ls命令,Windows上用的是dir命令。

<project>
	<profiles>
		<profile>
			<id>windows</id>
			<activation>
				<os>
					<family>windows</family>
				</os>
			</activation>
			<properties>
				<cmd.executable>echo</cmd.executable>
			</properties>
		</profile>
		<profile>
			<id>unix</id>
			<activation>
				<os>
					<family>unix</family>
				</os>
			</activation>
			<properties>
				<cmd.executable>ls</cmd.executable>
			</properties>
		</profile>
		<profile>
			<id>mac</id>
			<activation>
				<os>
					<family>mac</family>
				</os>
			</activation>
			<properties>
				<cmd.executable>ls</cmd.executable>
			</properties>
		</profile>
		<profile>
			<id>show-dir</id>
			<activation>
				<file>
					<exists>src/main/java</exists>
				</file>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>org.codehaus.mojo</groupId>
						<artifactId>exec-maven-plugin</artifactId>
						<version>1.6.0</version>
						<executions>
							<execution>
								<id>executable-executable</id>
								<phase>generate-sources</phase>
								<goals>
									<goal>exec</goal>
								</goals>
								<configuration>
									<executable>${cmd.executable}</executable>
								</configuration>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>
</project>

进阶常用功能

跳过/只跑某些测试

<project>
	<properties>
		<test.excludes>**/*ExcludeTest.java</test.excludes>
		<test.includes>**/*IncludeTest.java</test.includes>
	</properties>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M3</version>
        <configuration>
            <!--按照英文字母顺序跑测试-->
            <runOrder>alphabetical</runOrder>
            <!--设置为true就不跑测试,下面的模块不生效-->
            <skipTests>false</skipTests>
            <excludes>
                <exclude>${test.excludes}</exclude>
            </excludes>
            <includes>
                <include>${test.includes}</include>
            </includes>
        </configuration>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

然后在在命令行里面输入:

# 只跑MyTest开头的Java文件中的测试
$ mvn clean test -Dtest.includes=**/MyTest*.java

# 以MyTest开头的Java文件中的测试都不跑
$ mvn clean test -Dtest.exclude=**/MyTest*.java

打大jar包

有些时候,需要把自己的项目和依赖的外部项目合在一起变成一个jar包提供给别人,虽然不推荐这样做,姑且还是说下怎么做。

<build>
    <plugins>
          <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
            </configuration>
            <executions>
              <execution>
                <id>make-assembly</id>
                <!-- this is used for inheritance merges -->
                <phase>package</phase>
                <!-- bind to the packaging phase -->
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
    </plugins>
</build>

指定项目打包的路径

默认一个项目的jar包会生成在target目录下,如果想要更改生成路径,可以用maven-jar-plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <outputDirectory>/your/path/xxx</outputDirectory>
    </configuration>
</plugin>

maven-dependency-plugin

这个是比较常用的一种插件,功能比较多,这里先介绍copy-dependencies这个子功能。copy-dependencies能够在对项目的依赖进行操作,顾名思义,就是把依赖考拷贝到指定的某个地方。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>/your/path/xxx</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

清理

clean生命周期里,除了对maven生成的文件进行清理以外,还可以对自定义的文件进行清理

<plugin>
    <artifactId>maven-clean-plugin</artifactId>
    <configuration>
        <filesets>
            <fileset>
                <directory>/your/path/xxx</directory>
                <includes>
                    <include>**/*.jar</include>
                </includes>
            </fileset>
        </filesets>
    </configuration>
</plugin>

编译前检查

default生命周期里面第一个执行的阶段是validate,这里会做一些检查。比如检查jdk版本是否满足要求等。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.0.0-M1</version>
    <executions>
        <execution>
            <id>vulnerability-checks</id>
            <phase>validate</phase>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <!--jdk<1.8的话,停止后续操作,设为false的话输出警告信息-->
                <fail>true</fail>
                <rules>
                    <requireJavaVersion>
                        <version>1.8.0</version>
                    </requireJavaVersion>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

ToolChains(待研究)

额外功能

anltr3集成

在src/main/antlr3/xxx文件夹下放置词法和语法文件,xxx是词法文件里面定义的要生成的package路径,pom里面加入下面的配置,在generate-sources阶段会在target/generate-sources/antlr3目录下生成编译后的java文件

<project>
	<properties>
		<antlr3.version>3.5.2</antlr3.version>
	</properties>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.antlr/antlr-runtime -->
		<dependency>
		    <groupId>org.antlr</groupId>
		    <artifactId>antlr-runtime</artifactId>
		    <version>${antlr3.version}</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.antlr</groupId>
				<artifactId>antlr3-maven-plugin</artifactId>
				<version>${antlr3.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>antlr</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

thrift集成

仅以linux系统为例,更多可以参考root目录下的pom文件

<project>
	<profiles>
		<profile>
			<id>unix</id>
			<activation>
			    <!--操作系统为unix的时候触发-->
				<os>
					<family>unix</family>
				</os>
			</activation>
			<properties>
			    <!--下载文件地址-->
				<thrift.download-url>https://github.com/ccascone/mvn-thrift-compiler/raw/1.0_${thrift.version}/exe/thrift-linux-x86_64.exe</thrift.download-url>
				<!--下载文件后进行重命名-->
				<thrift.executable>thrift-${thrift.version}-unix-x86_64</thrift.executable>
				<!--不跳过下面的命令-->
				<thrift.skip-making-executable>false</thrift.skip-making-executable>
				<!--赋予可执行权限-->
				<thrift.exec-cmd.executable>chmod</thrift.exec-cmd.executable>
				<!--赋予可执行权限命令的参数-->
				<thrift.exec-cmd.args>+x ${project.build.directory}/tools/${thrift.executable}</thrift.exec-cmd.args>
			</properties>
		</profile>
		<profile>
			<id>thrift-generation</id>
			<activation>
			    <!--下面目录存在的时候触发-->
				<file>
					<exists>src/main/thrift</exists>
				</file>
			</activation>
			<build>
				<plugins>
					<plugin>
					    <!--去下载定义的thrift工具-->
						<groupId>com.googlecode.maven-download-plugin</groupId>
						<artifactId>download-maven-plugin</artifactId>
						<version>1.4.0</version>
						<executions>
							<execution>
								<id>get-thrift-executable</id>
								<phase>generate-sources</phase>
								<goals>
									<goal>wget</goal>
								</goals>
								<configuration>
									<url>${thrift.download-url}</url>
									<outputDirectory>${project.build.directory}/tools</outputDirectory>
									<outputFileName>${thrift.executable}</outputFileName>
								</configuration>
							</execution>
						</executions>
					</plugin>
					<!--执行修改权限的命令-->
					<plugin>
						<groupId>org.codehaus.mojo</groupId>
						<artifactId>exec-maven-plugin</artifactId>
						<version>1.6.0</version>
						<executions>
							<execution>
								<id>make-thrift-executable-executable</id>
								<phase>generate-sources</phase>
								<goals>
									<goal>exec</goal>
								</goals>
								<configuration>
									<skip>${thrift.skip-making-executable}</skip>
									<executable>${thrift.exec-cmd.executable}</executable>
									<commandlineArgs>${thrift.exec-cmd.args}</commandlineArgs>
								</configuration>
							</execution>
						</executions>
					</plugin>
					<!--用刚才下载的thrift工具生成java代码-->
					<plugin>
						<groupId>org.apache.thrift.tools</groupId>
						<artifactId>maven-thrift-plugin</artifactId>
						<version>0.1.11</version>
						<executions>
							<execution>
								<id>generate-thrift-sources</id>
								<phase>generate-sources</phase>
								<goals>
									<goal>compile</goal>
								</goals>
								<configuration>
									<generator>java</generator>
									<thriftExecutable>${project.build.directory}/tools/${thrift.executable}</thriftExecutable>
									<thriftSourceRoot>${basedir}/src/main/thrift</thriftSourceRoot>
								</configuration>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>
</project>

参考