说到Maven的入门使用,其实是特别简单的,如果只是说就是能使用,会使用Maven,也许只要短短的一两个小时就OK了,不需要去理解Maven的那些概念,而这篇文章就是要教会你会使用Maven,而整个系列则是要让你明白整个Maven。这篇文章就是如此,仅仅就是告诉你怎么用Maven,仅此而已,会用是学习整个系列的前提。
就像composer的composer.json、Make的makefile文件一样,Maven项目的核心是pom.xml文件。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。
现在我们不借助任何其它命令和IDE,来创建一个Maven项目。
首先,编写pom.xml文件。还是按照老规矩,从一个Hello World项目进行演示。以下就是创建项目的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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jellythink.HelloWorld</groupId> <artifactId>hello-world</artifactId> <version>1.0-SNAPSHOT</version> <name>hello-world</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project>
对于POM文件,现在细说一下。
project
元素, project
是所有pom.xml的根元素,它还声明了一些POM相关的命名空间及xsd元素; modelVersion
指定了当前POM模型的版本,对于Maven 2和Maven 3来说,它只能是4.0.0; groupId
、 artifactId
和 version
了,这三个是上述代码三个元素。这三个元素定义了一个项目的基本坐标,在Maven的世界里,所有的jar和war都是基于坐标进行区分的,会面的文章还会细说坐标; groupId
定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联,一般是使用组织或公司的域名;比如上面的 groupId
是 com.jellythink.HelloWorld
,其中 com.jellythink
就是我的网站域名倒过来写的,而 HelloWorld
则是整个项目的名称; artifactId
定义了当前Maven项目在组中唯一的ID,一般一个大项目组下面可能会包含多个子项目或子模块,而这个 artifactId
就是子项目或者子模块的名称; version
指定了这个项目当前的版本,后面的文章还会细说Maven中版本的含义; name
元素声明了一个对于用户更为友好的项目名称,方便后期的管理; properties
指定了Maven的一些重要属性,后续还会重点说这个属性的一些配置。 创建完pom.xml文件后,接下来就是创建代码文件了。在Maven中,有这样的一个约定,项目主代码都位于 src/main/java
目录,项目测试代码都位于 src/test/java
目录;接下来我们先按照这个约定分别创建目录,然后在代码目录创建 com/jellythink/HelloWorld/App.java
文件;在测试目录创建 com/jellythink/HelloWorld/AppTest.java
文件。
还是老规矩,我们在 App.java
中打印Hello World!,代码如下:
public class App { public String sayHello() { return "Hello World"; } public static void main( String[] args ) { System.out.println(new App().sayHello()); } }
同理,对于 AppTest.java
中编写以下单元测试代码:
public class AppTest { @Test public void testSayHello() { App app = new App(); String result = app.sayHello(); assertEquals("Hello World", result); } }
在Java中,我们进行单元测试时,基本上都是使用的JUnit,要使用JUnit这个包,我们就需要引入这个依赖包,此时,我们就需要在pom.xml中添加以下依赖内容:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies>
代码中添加了 dependencies
元素,该元素下可以包含多个 dependency
元素以声明项目的依赖。前面也说过, groupId
、 artifactId
和 version
是任何一个Maven项目最基本的坐标,JUnit也不例外; scope
表示依赖范围,后续的文章还会细说这个依赖和测试相关的内容。
万事俱备,只欠东风。接下来我们编译和测试。
搞定代码后,使用Maven进行编译,在项目根目录下运行 mvn clean compile
命令。执行输出如下图所示:
clean
告诉Maven清理输出目录target, compile
告诉Maven编译项目主代码。从输出中看到Maven首先执行了 clean:clean
任务,删除target目录。默认情况下,Maven构建的所有输出都在target目录中;接着执行 resources:resources
任务;最后执行 compiler:compile
任务,将项目主代码编译至target/classes目录。
上面说到的 clean:clean
、 resources:resources
和 compiler:compile
都对应了Maven的生命周期及插件,这个在后面还会有专题文章细说。
编译完成后,我们一般都会运行测试代码进行单元测试,虽然很多情况下,我们并没有这么做,但是我还是建议大家通过Maven做一些自动化的单元测试。
测试用例编写完毕之后就可以调用Maven执行测试,运行 mvn clean test
命令,输出如下:
从输出可以看到,Maven依次执行了 clean:clean
、 resources:resources
、 compiler:compile
、 resources:testResources
、 compiler:testCompile
和 surefire:test
。现阶段,我们需要明白这是Maven的生命周期的一个特性,这个生命周期后续还会细说。
到此,编译和测试均通过了,接着我们进行应用打包和运行。
打包就是将我们编写的应用打成JAR包或者WAR包。在我们的HelloWorld示例程序POM中,并没有指定打包类型,Maven则默认打包成JAR包。我们执行 mvn clean package
命令就可以完成打包。 mvn clean package
命令的输出如下:
可以看到,Maven在打包之前会执行编译、测试等操作,最后通过 jar:jar
任务负责打包。实际上就是jar插件的jar目标将项目主代码打包成一个名为hello-world-1.0-SNAPSHOT.jar的文件,这个最终生成的包会保存在target目录下,它是根据artifact-version.jar规则进行命名的;当然了,我们可以使用finalName属性来自定义该文件的名称。
到现在,我们得到了这个JAR包,如果别的项目要引用这个JAR包时,我们将这个JAR包复制到其它项目的classpath中就OK了。但是这样拷贝就违背了我们当初想要自动解决依赖的问题,所以如何才能让其它的Maven项目直接引用这个JAR包呢?我们需要执行 mvn clean install
命令。
从输出可以看到,在打包之后,又执行了安装任务 install:install
,最后将项目输出的JAR包安装到了Maven本地仓库中,我们可以在本地的仓库文件夹中能看到这个示例项目的pom和jar包。
到目前为止,通过这个示例项目体验了 mvn clean compile
、 mvn clean test
、 mvn clean package
和 mvn clean install
。执行test之前会先执行compile的,执行package之前会先执行test的,而类似地,install之前会执行package。我们可以在任何一个Maven项目中执行这些命令。
最后,不要忘了,我们生成的JAR包是有main方法的,也就是说这个JAR包是可以单独运行的;但是,由于带有main方法的类信息没有添加到manifest中,所以默认打包生成的jar是不能够直接运行的(使用jd-gui打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行)。为了生成可执行jar文件,需要借助 Apache Maven Shade Plugin 来完成,我们需要在pom.xml文件中以下插件配置:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.jellythink.HelloWorld.App</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
接下来,我们再执行 mvn clean install
命令,待构建完成之后在target目录下可以看到hello-world-1.0-SNAPSHOT.jar和original-hello-world-1.0-SNAPSHOT.jar,前面的是带有Main-Class信息的可运行jar,后者是原始的jar。我们执行以下命令:
java -jar hello-world-1.0-SNAPSHOT.jar
就可以正常执行。
上面非常详细的总结了如何全手动的创建一个Maven工程。通过上面的总结,我们大体可以总结以下几步:
src/main/java src/test/java
上面的三步我们成为Maven项目的骨架,也就是现在常说的“脚手架”。如果我们每创建一个Maven项目都需要把上面的步骤执行一次,确实很麻烦,那怎么办?程序就是解放人工的,让人来偷懒的。所以,在Maven中,我们可以通过Archetype生成项目骨架,将上面的步骤流程化。比如,现在我们要创建一个Maven项目,我们只需要输入以下命令就OK了。
mvn archetype:generate
执行这个命令后,后看到很多输出,有很多可用的Archetype供我们选择,每一个Archetype前面都会对应有一个编号,我们根据我们的需要,选择我们对应的骨架来创建项目就好了。然后再按照提示,输出新项目的groupId、artifactId、version和包名package,一个Maven项目就创建成功了。
我们在运行 mvn archetype:generate
时,实际上是在运行 maven-archetype-plugin
插件。
到此,这篇关于Maven的初级入门文章就到此总结完毕,这篇文章的知识点比较多,而且还很杂,如果看的有点不是很懂,也没有关系,后续的文章都会对这些你不懂的地方,你不熟悉的地方在进行深入的剖析和总结。当然了,也希望大家能通过这篇文章对Maven的使用有一个整体的认识,至少没有后续的文章,通过这篇文章,你也应该知道怎么使用Maven了,不是吗?
这一夜,深深的自责中……
果冻想,认真玩技术的地方。
2019年4月1日,于内蒙古呼和浩特。