本篇通过一个最基础的入门案例,熟悉一下maven最基础的使用方法。
编写POM
maven项目的核心是pom.xml文件,pom定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等等。
这里我们新建一个maven-demo-hello项目,并编写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>org.example</groupId>
<artifactId>maven-demo-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>maven-demo-hello</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
在上述pom定义中,最重要的事groupId、artifactId和version,这三个元素定了一个项目的基本坐标,在maven的世界中,任何jar、pom或war都是基于这些基本的坐标进行区分的。
- groupId定义项目属于哪个组,一般在公司中以公司域名或组织标识组成,比如com.mycom.myapp
- artifactId定义了当前项目在组中的唯一id,比如在上面例子中定义的是maven-demo-hello
- version指当前项目的版本,SNAPSHOT标识快照,说明当前项目处于开发中,是不稳定的版本,随着项目迭代,version会不断更新
没有任何实际的Java代码,我们就能够定义一个Maven项目的POM,这体现了Maven的一大优点,它能让项目对象模型最大程度地与实际代码相独立,我们可以称之为解耦,或者正交性。这在很大程度上避免了Java代码和POM代码的相互影响。比如当项目需要升级版本时,只需要修改POM,而不需要更改Java代码;而在POM稳定之后,日常的Java代码开发工作基本不涉及POM的修改。
编写主代码
项目的主代码和测试代码不同,项目主代码会被打包到最终的构件中(比如jar),而测试代码只在运行测试时用到,不会被打包。按照约定,maven主代码一般放在src/main/java目录下,在该目录下创建文件org/example/App.java,内容如下:
package org.example;
/**
* Hello world!
*
*/
package org.example;
/**
* Hello world!
*
*/
public class App
{
public String hello(){return "Hello World";}
public static void main( String[] args )
{
System.out.println( new App().hello() );
}
}
使用maven进行编译,在项目根目录下运行maven命令:mvn clean compile会得到如下输出:
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< org.example:maven-demo-hello >--------------------
[INFO] Building maven-demo-hello 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ maven-demo-hello ---
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ maven-demo-hello ---
[INFO] skip non existing resourceDirectory /Users/happy/mine-code/maven-demo-hello/src/main/resources
[INFO]
[INFO] --- compiler:3.11.0:compile (default-compile) @ maven-demo-hello ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 1 source file with javac [debug target 1.8] to target/classes
[WARNING] 未与 -source 8 一起设置引导类路径
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.006 s
[INFO] Finished at: 2024-01-07T19:36:16+08:00
[INFO] ------------------------------------------------------------------------
- clean告诉Maven清理输出目录target/
- compile告诉Maven编译项目主代码
- 从输出中看到Maven首先执行了clean:clean任务,删除target/目录
- 默认情况下,Maven构建的所有输出都在target/目录中
- 接着执行resources:resources任务(未定义项目资源,暂且略过)
- 最后执行compiler:compile任务,将项目主代码编译至target/classes目录
上文提到的clean:clean、resources:resources和compiler:compile对应了一些Maven插件及插件目标,比如clean:clean是clean插件的clean目标,compiler:compile是compiler插件的compile目标
除了使用maven命令的方式运行maven项目之外,日常开发中使用IDE,可以直接界面上操作即可,如图:
编写测试代码
Maven项目中默认的测试代码目录是src/test/java,测试代码需要使用到JUnit,这里我们需要引入JUnit的依赖,在pom中添加依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
有了这段依赖,maven自动下载junit包,具体从哪里下载取决于maven配置,一般在企业中会有对应的私服仓库地址
上述POM代码中还有一个值为test的元素scope,scope为依赖范围,若依赖范围为test则表示该依赖只对测试有效。换句话说,测试代码中的import JUnit代码是没有问题的,但是如果在主代码中用import JUnit代码,就会造成编译错误。
如果不声明依赖范围,那么默认值就是compile,表示该依赖对主代码和测试代码都有效。
编写单元测试,代码如下:
package org.example;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest{
@Test
public void helloTest(){
App app = new App();
String result = app.hello();
assertEquals("Hello World", result);
}
}
使用maven命令执行测试,运行:mvn clean test,输出如下:
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< org.example:maven-demo-hello >--------------------
[INFO] Building maven-demo-hello 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ maven-demo-hello ---
[INFO] Deleting /Users/happy/mine-code/maven-demo-hello/target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ maven-demo-hello ---
[INFO] skip non existing resourceDirectory /Users/happy/mine-code/maven-demo-hello/src/main/resources
[INFO]
[INFO] --- compiler:3.11.0:compile (default-compile) @ maven-demo-hello ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 1 source file with javac [debug target 1.8] to target/classes
[WARNING] 未与 -source 8 一起设置引导类路径
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ maven-demo-hello ---
[INFO] skip non existing resourceDirectory /Users/happy/mine-code/maven-demo-hello/src/test/resources
[INFO]
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ maven-demo-hello ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 1 source file with javac [debug target 1.8] to target/test-classes
[WARNING] 未与 -source 8 一起设置引导类路径
[INFO]
[INFO] --- surefire:3.1.2:test (default-test) @ maven-demo-hello ---
[INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.example.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.072 s -- in org.example.AppTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.165 s
[INFO] Finished at: 2024-01-07T20:13:30+08:00
[INFO] ------------------------------------------------------------------------
可以看到compiler:testCompile,surefire:test插件运行成功,并输出了测试报告,显示一共运行了多少测试,失败多少,出错了多少,跳过多少。
打包和运行
项目编译、测试都通过之后,下一个重要步骤就是打包(package)。本项目pom中没有指定打包类型,使用默认的打包类型jar。
使用maven命令:mvn clean package 进行打包,输出如下:
[INFO] --- jar:3.3.0:jar (default-jar) @ maven-demo-hello ---
[INFO] Building jar: /Users/happy/mine-code/maven-demo-hello/target/maven-demo-hello-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.015 s
[INFO] Finished at: 2024-01-07T20:24:22+08:00
[INFO] ------------------------------------------------------------------------
类似地,在执行打包之前,会先执行编译、测试等操作,这里看到jar:jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个名为maven-demo-hello-1.0-SNAPSHOT.jar的文件。
至此,我们拿到了整个项目的输出,可以通过maven命令:mvn clean install 命令将输出的构件安装到本地仓库,这样其他项目也可以使用该构件。在企业中,一般会通过流水线或构建平台等将包发布到企业私有仓库中。
默认打包生成的jar是不能够直接运行的,因为带有main方法的类信息不会添加到manifest中(打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行)。为了生成可执行的jar文件,需要借助maven-shade-plugin,在pom中配置该插件如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>
org.example.App
</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
再次运行maven打包命令:mvn clean package,输出如下:
[INFO] --- jar:3.3.0:jar (default-jar) @ maven-demo-hello ---
[INFO] Building jar: /Users/happy/mine-code/maven-demo-hello/target/maven-demo-hello-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- shade:1.2.1:shade (default) @ maven-demo-hello ---
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /Users/happy/mine-code/maven-demo-hello/target/maven-demo-hello-1.0-SNAPSHOT.jar with /Users/happy/mine-code/maven-demo-hello/target/maven-demo-hello-1.0-SNAPSHOT-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.223 s
[INFO] Finished at: 2024-01-07T20:40:00+08:00
[INFO] ------------------------------------------------------------------------
可以看到maven-demo-hello-1.0-SNAPSHOT.jar和original-maven-demo-hello-1.0-SNAPSHOT.jar,前者是带有Main-Class信息的可运行jar,后者是原始的jar。
运行jar包:
➜ target java -jar maven-demo-hello-1.0-SNAPSHOT.jar
Hello World
➜ target java -jar original-maven-demo-hello-1.0-SNAPSHOT.jar
original-maven-demo-hello-1.0-SNAPSHOT.jar中没有主清单属性
【Maven笔记1】Maven介绍
【Maven笔记2】Maven安装与配置