用了一段时间的maven,但是只是简单的使用它来解决依赖问题以及打包而已,配置文件只是使用了<dependency>
这么一个配置。最近好好接触了一下maven的生命周期和插件机制,被其强大的可配置性所折服。此处简要记录自己所感并分享。
简单接触
maven中所有的插件都是配置在标签<build>
–><plugins>
下面,其下每一个<plugin>
标签对应一个插件。官方自带的插件五花八门,用的最多的无非是maven-compiler-plugin
、maven-dependency-plugin
、maven-surefire-plugin
等。官方网址
每一个插件可以定义插件的groupId
与artifactId
,以及要执行的<executions>
。executions
下可以定义一大堆的execution
用来定义具体的任务,其中又包括目标、绑定的阶段、配置等参数。大致上配置文件如下:
xml
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <executions> <execution> <id>compile</id> <goals> <goal>compile</goal> </goals> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <id>get_source</id> <goals> <goal>get</goal> <goal>unpack</goal> </goals> <phase>process-resources</phase> <configuration> <artifact>com.alibaba.middleware.race:rpc-api:1.0:jar:sources</artifact> <artifactItems> <artifactItem> <groupId>com.alibaba.middleware.race</groupId> <artifactId>rpc-api</artifactId> <version>1.0</version> <classifier>sources</classifier> </artifactItem> </artifactItems> <outputDirectory>${project.build.sourceDirectory}</outputDirectory> <excludes>META-INF\/**</excludes> </configuration> </execution> </executions> <!--<configuration>--> <!--<artifact>com.alibaba.middleware.race:rpc-api:1.0:jar:sources</artifact>--> <!--</configuration>--> </plugin> </plugins> </build>
其中maven-dependency-plugin
插件下的任务先从中央库下载com.alibaba.middleware.race:rpc-api:1.0:jar:sources
的jar包,再解压到指定的目录并排除META-INF
文件夹。(由于本人刚接触,这种写法可能不是最佳实践,如果有人有更好的写法的话恳请告知。)这个id为get_source
的任务绑定到了阶段process-resources
,在执行到default
生命周期的该阶段时会执行这个任务。运行mvn compile
,成功执行,感觉良好。
命令行执行
现在想要在命令行下不是执行某个阶段,而是直接执行某个插件的任务。命令
mvn org.apache.maven.plugins:maven-dependency-plugin:get -Dartifact=com.alibaba.middleware.race:rpc-api:1.0:jar:sources
或者其缩写
mvn dependency:get -Dartifact=com.alibaba.middleware.race:rpc-api:1.0:jar:sources
成功下载了指定的源文件。看来命令行执行插件也很容易,但是有一个小问题:我没办法不指定artifact
,也就是直接执行
mvn dependency:get
会有错误。但是我想使用配置文件中的artifact配置,而不想每次运行都敲那么一大串(不使用周期绑定的情况下)。
我尝试了在上面的配置文件中加入了注释掉的那部分内容,直接运行mvn dependency:get
,没出错,似乎可行。
想要更灵活的命令行执行
注释部分的内容虽然可行,dependency
插件的get
任务虽然找到了该配置,但是缺点也很明显:
- 我没办法针对不同的任务使用不同的配置;
- 我没办法一条命令运行两个任务。
对于1,假如现在我有另一个id为get_sources2
的任务,它是把com.alibaba.middleware.race:rpcapi:2.0:jar:sources
这么一个版本的包下载并拷贝到相同目录下,而且和get_sources
只能运行一个。这样的话明显的不能使用“全局的配置”,这样会有冲突;而在每个execution
中定义的参数,是没办法被mvn dependency:get
这样识别的。
对于2,我要想执行“下载并解压到指定目录”这个操作,必须执行:
mvn dependency:get
mvn dependency:unpack ##(当然,需要先向get那样配置参数)
两条语句虽然不多,但是当任务中又有copy
等其他goal
时,也是相当麻烦的。
最为理想的状态是:有这么一条命令,能够让我指定运行时的某个插件具体的任务,而不是仅仅运行某个目标。比如类似如下的命令:
mvn dependency:get_sources ##(get_sources为某个execution的id)
这样的话,这两个问题都完美解决了(不绑定生命周期的阶段情况下)。但是很遗憾,maven不支持这种操作。
不过倒是有一种办法可以解决问题1:使用默认的execution id
名:default-cli
。凡是execution id
为default-cli
的任务,命令行运行时如果goal
在goals
中有定义,则goal与其参数会被执行。这其实是将“全局配置”放在了“默认配置”中。好处在于可以定义几个任务,然后需要执行哪个任务,只需要把<id>
改为default-cli
就行了。虽然要修改pom.xml
,但是也不算麻烦。
对于问题2,如果存在任务之间的依赖定义的话,倒是可以把目标get
和unpack
分别定义在不同的任务中,然后任务中指定依赖关系:unpack
依赖get
,这样只需要执行unpack
即可自动执行get
,就像ant中<target>
的depends
那样。但是我没有找到如何定义这种“任务间的依赖”。
更深入的学习
当然,并非没有可能简化:如果能自己写一个插件的话,一切问题都不在话下。但是现在自己的水平还远远没有到那种程度。学习之路依旧遥远。
PS:Maven相比于ant的很大的进步之处在于其周期和阶段的概念。除了极少数不适合绑定阶段的任务外还是多使用阶段和任务的绑定比较好,能省很多麻烦。
PPS:本人学习maven未深,理解可能出现偏差,如果有人发现文中的疏漏或者错误,恳请指正以免误导他人。