上周四下午,放翁同学在TOP群中问了这样一个小问题,聊天记录摘录如下:
有同学知道如何用maven来将第三方jar的代码也打入到生成的项目jar中?就好比eclipse可以直接将部分依赖打入到项目jar中
一看到打包,对于jar类型的项目,运行mvn package,就等同于运行mvn jar:jar,那放翁同学的问题,一定是与jar插件有关了,在Maven官方网站,查阅了一下jar插件的相关文档,未果。
临近下班时,我找放翁同学当面了解了一下他真正的需求,就是把该项目的依赖的源码都打入到一个可执行的jar包中,我看了看Maven官方网站的插件列表,以前对assembly插件有点印象,这个插件或许可以胜任放翁的需求,就直接告诉他,试试这个插件吧,第二天早上上班过来,一问放翁,这个东东确实管用,解决了放翁同学的问题。
我以前看过这个插件的文档,但自己却没有使用过,自己也动手Demo一下,和大家分享一下吧
- 创建一个jar项目
tonglin@tonglin-desktop:~$ mvn archetype:generate [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Default Project [INFO] task-segment: [archetype:generate] (aggregator-style) [INFO] ------------------------------------------------------------------------ [INFO] Preparing archetype:generate [INFO] No goals needed for project - skipping [INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'. [INFO] Setting property: velocimacro.messages.on => 'false'. [INFO] Setting property: resource.loader => 'classpath'. [INFO] Setting property: resource.manager.logwhenfound => 'false'. [INFO] [archetype:generate] [INFO] Generating project in Interactive mode [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0) Choose archetype: ...... 15: internal -> maven-archetype-quickstart () ...... Choose a number: (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41) 15: : Define value for groupId: : com.taobao.top.appstore Define value for artifactId: : maven-test Define value for version: 1.0-SNAPSHOT: : Define value for package: com.taobao.top.appstore: : Confirm properties configuration: groupId: com.taobao.top.appstore artifactId: maven-test version: 1.0-SNAPSHOT package: com.taobao.top.appstore Y: : [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating OldArchetype: maven-archetype-quickstart:RELEASE [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: groupId, Value: com.taobao.top.appstore [INFO] Parameter: packageName, Value: com.taobao.top.appstore [INFO] Parameter: package, Value: com.taobao.top.appstore [INFO] Parameter: artifactId, Value: maven-test [INFO] Parameter: basedir, Value: /home/tonglin [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] ********************* End of debug info from resources from generated POM *********************** [INFO] OldArchetype created in dir: /home/tonglin/maven-test [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 minute 27 seconds [INFO] Finished at: Tue Dec 22 11:33:19 CST 2009 [INFO] Final Memory: 8M/14M [INFO] ------------------------------------------------------------------------
通过archetype插件的交互操作,我们创建了一个jar项目,名字叫做maven-test
该原型默认会创建一个包含main函数的类,com.taobao.top.appstore.App,源代码如下:package com.taobao.top.appstore; /** * Hello world! * */ public class App { public static void main( String[] args ) { System.out.println( "Hello World!" ); } }
这个类很简单,如果我们想生成可执行的jar文件,需要修改MENIFEST文件,这里我们需要配置jar插件,编辑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/maven-v4_0_0.xsd"> ...... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.taobao.top.appstore.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> ...... </project>
- 打包,创建可执行的jar文件
tonglin@tonglin-desktop:~/maven-test$ mvn package
查看jar文件的MENIFEST文件内容:
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Created-By: Apache Maven Built-By: tonglin Build-Jdk: 1.6.0_16 Main-Class: com.taobao.top.appstore.App
- 运行
tonglin@tonglin-desktop:~/maven-test$ java -jar target/maven-test-1.0-SNAPSHOT.jar Hello World!
运行成功,这个类很简单,没有任何依赖,如果我们想做些更复杂的东西,首先我们需要
- 增加依赖
<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"> ...... <dependencies> ...... <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.1</version> </dependency> ...... </dependencies> ...... </project>
- 生成Eclipse文件
tonglin@tonglin-desktop:~$ cd maven-test/ tonglin@tonglin-desktop:~/maven-test$ mvn eclipse:clean eclipse:eclipse
- 打开Eclipse,编辑com.taobao.top.appstore.App
package com.taobao.top.appstore; import org.apache.commons.lang.StringUtils; /** * Hello world! * */ public class App { public static void main( String[] args ) { final String x = StringUtils.center("Hello World!", 20); System.out.println( x ); } }
- 再次打包,运行
tonglin@tonglin-desktop:~/maven-test$ mvn package tonglin@tonglin-desktop:~/maven-test$ java -jar target/maven-test-1.0-SNAPSHOT.jar Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils at com.taobao.top.appstore.App.main(App.java:13) Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang.StringUtils at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 more
运行失败,抛出异常说org.apache.commons.lang.StringUtils类找不到,我们需要将org.apache.commons.lang.StringUtils,添加到类路径,修改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/maven-v4_0_0.xsd"> ...... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>com.taobao.top.appstore.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> ...... </project>
- 再次打包
tonglin@tonglin-desktop:~/maven-test$ mvn package
查看jar包的MENIFEST
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Created-By: Apache Maven Built-By: tonglin Build-Jdk: 1.6.0_16 Main-Class: com.taobao.top.appstore.App Class-Path: commons-lang-2.1.jar
- 运行
tonglin@tonglin-desktop:~/maven-test$ java \-jar target/maven-test-1.0-SNAPSHOT.jar Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils at com.taobao.top.appstore.App.main(App.java:13) Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang.StringUtils at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 more
运行仍旧失败,那如何把依赖的源代码也打到这个jar包中呢,这个时候,maven-assembly-plugin插件就派上用场了
- 配置maven-assembly-plugin
<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"> ...... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>com.taobao.top.appstore.App</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.taobao.top.appstore.App</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build> ...... </project>
注意上面的jar-with-dependencies,这样就会把jar包和它的所有runtime依赖添加到一个jar包中了,
- 装配
tonglin@tonglin-desktop:~/maven-test$ mvn assembly:assembly [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'assembly'. [INFO] ------------------------------------------------------------------------ [INFO] Building maven-test [INFO] task-segment: [assembly:assembly] (aggregator-style) [INFO] ------------------------------------------------------------------------ [INFO] Preparing assembly:assembly [INFO] ------------------------------------------------------------------------ [INFO] Building maven-test [INFO] ------------------------------------------------------------------------ [INFO] [resources:resources] [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /home/tonglin/maven-test/src/main/resources [INFO] [compiler:compile] [INFO] Nothing to compile - all classes are up to date [INFO] [resources:testResources] [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /home/tonglin/maven-test/src/test/resources [INFO] [compiler:testCompile] [INFO] Nothing to compile - all classes are up to date [INFO] [surefire:test] [INFO] Surefire report directory: /home/tonglin/maven-test/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.taobao.top.appstore.AppTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.089 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0添加到类路径 [INFO] [jar:jar] [INFO] Building jar: /home/tonglin/maven-test/target/maven-test-1.0-SNAPSHOT.jar [INFO] [assembly:assembly] [INFO] Processing DependencySet (output=) [INFO] Building jar: /home/tonglin/maven-test/target/maven-test-1.0-SNAPSHOT-jar-with-dependencies.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9 seconds [INFO] Finished at: Tue Dec 22 13:56:56 CST 2009 [INFO] Final Memory: 13M/24M [INFO] ------------------------------------------------------------------------
运行上面的命令,就会在target目录下生成maven-test-1.0-SNAPSHOT-jar-with-dependencies.jar
- 运行
tonglin@tonglin-desktop:~/maven-test$ java -jar target/maven-test-1.0-SNAPSHOT-jar-with-dependencies.jar Hello World!
运行成功,一切OK,如果我们想在打包阶段,自动运行assembly插件的assembly目标,那么只需少许的改动
- 打包阶段自动运行assembly
<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"> ...... <build> <plugins> ...... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.taobao.top.appstore.App</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> ...... </plugins> </build> ...... </project>
这样,我们再次运行:
tonglin@tonglin-desktop:~/maven-test$ mvn clean package [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Building maven-test [INFO] task-segment: [clean, package] [INFO] ------------------------------------------------------------------------ [INFO] [clean:clean] [INFO] Deleting directory /home/tonglin/maven-test/target [INFO] [resources:resources] [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /home/tonglin/maven-test/src/main/resources [INFO] [compiler:compile] [INFO] Compiling 1 source file to /home/tonglin/maven-test/target/classes [INFO] [resources:testResources] [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /home/tonglin/maven-test/src/test/resources [INFO] [compiler:testCompile] [INFO] Compiling 1 source file to /home/tonglin/maven-test/target/test-classes [INFO] [surefire:test] [INFO] Surefire report directory: /home/tonglin/maven-test/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.taobao.top.appstore.AppTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.077 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [jar:jar] [INFO] Building jar: /home/tonglin/maven-test/target/maven-test-1.0-SNAPSHOT.jar [INFO] [assembly:single {execution: make-assembly}] [INFO] Processing DependencySet (output=) [INFO] Building jar: /home/tonglin/maven-test/target/maven-test-1.0-SNAPSHOT-jar-with-dependencies.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 13 seconds [INFO] Finished at: Tue Dec 22 14:40:54 CST 2009 [INFO] Final Memory: 17M/34M [INFO] ------------------------------------------------------------------------
注意:上面配置package,运行的是assembly:single,而不是assembly:assembly,这两者有什么区别呢:
- assembly:assembly 这个目标会自动执行package生命周期
- assembly:single 这个目标仅会装配成jar-with-dependencies,我们不想让package阶段运行两次package,所以配置成这个
- 参考资料:
- maven-assembly-plugin:http://maven.apache.org/plugins/maven-assembly-plugin/
- maven-jar-plugin:http://maven.apache.org/plugins/maven-jar-plugin/
- 项目源代码:
- Windowd平台:maven-test.zip
- Linux平台:maven-test.tar.gz
希望这篇文章能够让有同样经历的同学,减少困惑,有任何问题请联系我!