通过前两篇的介绍我们基本上对Maven有了一个初入的了解,在一这一篇中,我们详细介绍一下如何在项目中使用Maven,也就是Maven的入门。
什么是POM
在Maven实战-第一篇(Maven的基本信息)中我们介绍过Make和Ant。它们都是通过Makefile或者build.xml来管理项目的。那么对于Maven也是一样的,Maven也需要一个脚本文件来执行项目的构建和管理。在Maven中采用了和Ant一样的管理方式,也就是xml文件的方式进行项目的管理。只不过在语法中有些区别。并且在Maven中命令规则为pom.xml,而不是Ant中的build.xml。
下面我们按照软件开发的惯例,创建一个Helloworld空项目。具体操作如图所示:
然后我们在这个项目的根目录中创建一个pom.xml
文件。
我们IDEA中提示了我们pom.xml
文件报错了,这是因为pom.xml
文件中有Maven约定的语法规则,我们现在的pom.xml
文件是空的所以报错了。pom.xml
文件中可以配置的语法非常的多,我们下面介绍一下,pom.xml
文件中必须要设置的内容。
- xml:指定了xml文件的版本和编码。
- project:该标签是pom.xml项目的的根标签。
- modelVersion:指定POM模型的版本。对于Maven3来说,必须是4.0.0。
- groupId:指定我们创建的这个项目是属于哪个组的,通常为公司组织的域名倒写,这样保证不会重复。
- artifactId:定义了当前Maven项目在组中的唯一标识,也就是同组唯一。
- version:指定当前项目的版本。在Maven中有两个类型的版本,一个是正常的版本需要,还有一个为SNAPSHOT快照版本。快照版本为不稳定版本。
- name:项目的名称。
我们先简单有一个概念,在后续的章节中,我们会详细介绍上面的内容。下面我们看一下例子,来更直观的感受到,在Maven中怎么配置上面介绍的信息。
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven实战</name>
</project>
当我们配置完上述内容时,我们发现pom.xml
中已经不显示报错信息了,这就表明我们配置的没有任何问题了,如果还是报错,而表示我们有的标签配置的还是有误。
代码编写
在我们完成上面的配置后,我们可以开始我们的代码编写了。但使用Maven编写代码时,和我们普通的Jave项目有一些区别,它也有相应的默认的约定。我们知道在普通的项目中所有Jave源码代码都写在项目的src
目录中。在Maven中同样的对项目的源代码目录进行了约定,只不过在Maven中对普通项目的源代码称之为主代码。还约定了测试代码的目录。主代码会最终打包到构建Jar包中的代码。而测试代码只在测试运行时用到,不会被打包。在Maven中使用下面两个目录结构进行区分。
- 主代码
src/main/java
上述目录为Maven默认约束好的主代码的目录,只要按照上面的路径设置,Maven会自动编译上述目录中的代码并打包到Jar构建包中。
- 测试代码
src/test/java
上述目录为Maven默认约束好的测试代码的目录,Maven只会在测试运行时编译上述目录中的代码并且不会打包到Jar构建包中。
有一点我们要注意,上面所说的主代码或者测试代码的目录都是Maven默认约定的目录结构,我们可以通过pom.xml
中提供的语法更改默认的目录结构,但在实际的项目中我们并不会这么做。
Maven命令
按照上面介绍的我们先在项目中创建一个src/main/java
目录。
然后我们在创建一个新的Java类,来测试一下,代码能否执行成功。
当我们要创建Java类,发现IDEA中已经没有我们想要创建Java类的那个菜单了。这是因为IDEA认为src
才是源代码的目录。所以没有显示可以创建Java的菜单。要想可以显示出创建Java的菜单也非常的简单,也就是告诉IDEA此时src/main/java
目录就是项目的源代码目录即可。具体配置如下:
这样IDEA就会识别出我们现在选择的这个目录是源码目录,就允许我们创建Java文件。
我们首先创建一个简单的Java类,来验证一下,项目是否能成功的执行的我们的代码。
- Mazhe
public class Mazhe {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven");
}
}
- 输出
Hello World Mazhe Maven
下面我们还是改造一下代码,按照第一篇中例子一样,我们创建一个对象,然后将对象转换成JSON字符串。
- Userinfo
public class Userinfo {
private String username;
private String passworld;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassworld() {
return passworld;
}
public void setPassworld(String passworld) {
this.passworld = passworld;
}
@Override
public String toString() {
return "Userinfo{" +
"username='" + username + '\'' +
", passworld='" + passworld + '\'' +
'}';
}
}
- Mazhe
public class Mazhe {
public static void main(String[] args) {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("mazhe");
userinfo.setPassworld("maven");
System.out.println(userinfo);
}
}
- 输出
Userinfo{username='mazhe', passworld='maven'}
下面我们按照Maven的方式引入依赖包,这样就不用向之前那样,我们需要在项目中手动的创建依赖包的目录和依赖包的拷贝工作了。那我们上哪去查询依赖包呢?这个不用担心,Maven为了方便我们查询,可以通过在线的网站上通过关键字搜索查询。下面是依赖包的网站的地址:
我们可以在上面搜索中直接搜索我们想要的依赖包。按照需求,我们需要将对象转换成json。这样的工具类很多,我们使用fastjson的方式实现。所以我们只需要在上面的网站上搜索即可。
然后我们点击上面的链接,就会显示出这个依赖包所有可以使用的版本。
我们随便选择一个版本即可。在实际的软件开发中,我们需要选择相对比较稳定的版本,这样的版本问题比较少。那如何选择文档的版本呢。我们可以通过上图中的Usages
参数来观察,这个参数越多,则表示使用的人越多。
当我们选择一个版本后,点击链接就会显示出我们使用这个依赖包具体的Maven配置信息了。并且,通过上图中我们可以观察到,除了Maven外,它还支持其它的项目管理工具,例如Gradle。我们点击这个标签就会显示Gradle的配置信息。
下面我们介绍一下,如何使用上面的Maven配置。我们拷贝一下fastjson的Maven配置信息,然后粘贴到项目的pom.xml文件中。之前我们介绍过,pom.xml是项目的构建文件,所以所有和Maven相关的配置都可以在pom.xml文件中配置。
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven实战</name>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.35</version>
</dependency>
</project>
当我们将上面的配置粘贴到pom.xml后,发现文件居然报错了。
这是因为上面的配置不符合Maven的语法规则。具体的原因是因为Maven中需要将所有的依赖配置都放到<dependencies></dependencies>
标签中。也就是如下配置才可以:
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven实战</name>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.35</version>
</dependency>
</dependencies>
</project>
当我们这样配置完后成后,发现依赖包的版本显示红色了,这是因为Maven还没有查找到这个依赖包。
这是因为IDEA暂时还没有将我们这个项目识别出Maven项目。所以需要选中pom.xml文件,然后右键选择:Add as Maven Project
当我们这样操作完成后,我们发现我们的版本已经不会报错了。
并且这时我们看项目的依赖包,发现Maven已经自动将fastjson依赖的所有包都下载成功了。
下面我们在项目中使用一下,看看用Maven引入依赖包的方式和我们自己引用有没有什么区别。
- Mazhe
import com.alibaba.fastjson.JSONObject;
public class Mazhe {
public static void main(String[] args) {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("mazhe");
userinfo.setPassworld("maven");
System.out.println(JSONObject.toJSONString(userinfo));
}
}
- 输出
{"passworld":"maven","username":"mazhe"}
我们看日志输出,成功的将对象转换成json了,并且在我们使用Maven和我们自己引入依赖的方式在编写代码上没有任何区别。但在使用上,我们可以很直观的感受到使用Maven引入依赖包时,为我们带来的便捷。
- 编译
下面我们介绍一下,Maven提供的其它的功能,我们知道在项目中我们比较常见的操作就是代码测试、编译、打包、运行等操作。上面我们介绍了如何使用Maven引入我们需要的依赖包。下面我们介绍一下,如何使用Maven编译项目。
- Mazhe
import com.alibaba.fastjson.JSONObject;
public class Mazhe {
public static void main(String[] args) {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("mazhe");
userinfo.setPassworld("maven");
System.out.println(JSONObject.toJSONString(userinfo));
}
}
在Maven中编译项目很简单,特别是使用是IDEA这样的开发工具,我们只需要简单保存一下就可以完成代码的编译了。所以为了加深我们的理解,今天我们使用Maven命令的方式编译项目,这也是我们介绍Maven的第二个命令(第一个Maven命令为:):
mvn clean compile
当我们在项目的根目录中输入完上面的命令后,Maven就会自动的执行项目的编译。
当我们输入完上面的Maven命令后,Maven就会开始工作了,并且通过上图中,我们也可以发现Maven执行时输出的日志信息。
当项目编译成功后就会在项目的根目录中创建target目录,然后将主代码的中编译好的class文件放到target中的classes目录下。也就上图所示。
- 测试
下面我们介绍一下如何使用Maven进行代码的测试。在Maven中如果要想执行测试代码,需要引入额外的依赖包。因为我们上面的内容中已经介绍了如何在项目中引入依赖包,所以在这里我们就不做过说的介绍了。在Maven中要执行测试,需要我们使用junit依赖。引入junit依赖的配置信息如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
同样的,我们还是需要将上面的配置信息配置到pom.xml文件中。下面我们开始编写测试类的代码。这里有一点要我们注意,按照Maven的约定,测试类的代码是需要放在测试类代码src/test/java
目录中的,而不是我们刚刚写的主代码的目录中,这一点要特别的注意。
同样的我们在项目中创建一个src/test/java
目录。
然后我们继续创建一个测试类,但当我们右键创建时,发现了类似的问题,就是没有提供创建Java类的菜单,这个问题和上面遇到的问题一样,是因为IDEA并没有识别出我们这个测试类的目录,同样的我们使用下面的方式配置一下即可。
这里有一点选择和上面的不同,就是因为这次我们创建的是测试类的目录,所以我们要选择Test Rources Root
,而不是之前我们选择的Sources Root
。
当我们设置完成后,在右键创建时,我们发现这时IDEA可以允许我们创建Java类了。下面我们创建一个测试类,具体的代码如下:
import org.junit.Test;
public class MazheTest {
@Test
public void maven(){
System.out.println("Hello world Mazhe Maven Test");
}
}
我们简单说明一下上面的代码。因为这里面和我们平常写的代码还是有一些不一样的。例如我们之前要写可以执行的代码时,需要写public static void main(String[] args)
方法才可以执行。而当我们使用junit进行测试时代码时,对可以执行的方法名称并没有要求,可以随便定义符合Java规范的方法名,然后在对应的方法上添加@Test
注解即可,这样这个方法就可以使用Maven进行测试了,也可以我们通过右键的方式直接运行。下面我们执行一下:
我们看日志的输入成功的输出了我们代码中的结果。下面我们还是使用Maven命令的方式来执行一下我们测试的代码,要如何使用呢?很简单,我们还是通过命令的方式即可。在Maven中执行测试的命令为:
mvn clean test
我们在项目的根目录中执行一下上面的命令,来看一下输出的结果。
我们看日志输出,不但成功的输出了我们测试代码中的内容,还输出了我们本次运行了多少测试,运行了多少和失败了多少等统计的信息。这也就是使用Maven非常方便的体现。下面我们将上面的代码手动抛出一个异常,然后在执行一下测试用例在看一下,这次Maven会输入什么样的信息。
- MazheTest
import org.junit.Test;
public class MazheTest {
@Test
public void maven() {
System.out.println("Hello world Mazhe Maven Test");
System.out.println(1 / 0);
}
}
我们手动输出了一个除以0的操作,所以上面的代码会抛出异常,我们看这时Maven的日志输出,会不会成功的输入异常上面的内容。
我们看异常上面的代码也成功的输出了,但是是执行下面的代码报错了。并且Maven已经将错误的信息输出。下面我们在看一下Maven中测试用例的统计信息,我们可以看到显示运行了一个,错误的一个。
下面我们详细介绍一下pom.xml中配置的标签:
-
dependencies:声明项目的依赖的父标签,也就是上面介绍的,所有依赖包的配置必须在这个配置标签下面才可以使用。
-
dependency:声明项目的依赖的子标签,也就是我们上面说的依赖的配置标签。
-
groupId:依赖的基本坐标,通常为组织的域名倒写。目的就是唯一不能重复。
-
artifactId:依赖的基本坐标,通常为该项目的唯一标识。不同组织下可以重复,相同项目中不可以重复。
-
version:依赖的基本坐标,项目的不同版本号。不同版本中依赖包的内容,可能会有很大的不同,所以我们在通过Maven使用依赖包时,版本要特别的注意,甚至不同依赖包版本与版本不同,可能会导致项目冲突的问题。
-
打包
下面我们介绍一下如何在Maven中执行项目的打包。在Maven中执行打包也非常的方便,只要执行下面的命令即可,Maven就会自动在target目录下生成相应的jar包。因为jar包是Maven默认的打包类型。所以我们不需要添加额外的配置,当然我们也可以指定其它的打包类型。在后面的章节中我在做过多的介绍。下面是Maven打包的命令:
mvn clean package
当我们执行完上面的命令后,Maven就会将打包完的jar包放到target目录下。
我们通过上面的Maven日志输出可以很方便的看到,这个jar包生成的目录地址。我们直接进入到target目录中,也可以直接看到我们刚刚打包完的jar包。
Maven默认的打包命名规则为当前项目的名称。当然我们也可以通过Maven中提供的finalName
来自定义该打包文件的名称。具体配置如下:
- pom.xml
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven实战</name>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.35</version>
</dependency><dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
<build>
<finalName>Mazhe-Maven-1.0</finalName>
</build>
</project>
然后我们继续执行mvn clean package
命令后在看一下打包后的jar包是否为我们配置中指定的名称。
我们看上图中的结果,我们成功的指定了我们打包的名称了,这也就是finalName
标签的作用。
- 安装
下面我们继续介绍另外的Maven命令,也就是安装命令。这里所说的安装可以理解为将打包好的jar安装到你的电脑中。这样当其它的项目相要引入这个依赖包时,只需要在对应的项目的pom.xml中配置即可。在Maven中可以使用下面命令进行安装:
mvn clean install
当我们执行完上面的命令后,Maven依然会在项目的target中生成相应的jar包。
除此之外Maven还会将这个jar包生成到本地的仓库中,也就是上图中Installing
输出的目录信息。由于Maven中涉及仓库的内容,我们会在后续的内容中详细的介绍,所以我们暂时可以先理解当我们执行完上面的命令后,Maven就会将这个jar包安装到我们的Maven指定的目录中,并且这个目录可以允许其它项目通过依赖配置的方式引入我们这个依赖包。
- 运行
下面我们介绍一下,如果在Maven中直接运行项目中的main方法,如果要实现上面的需求,需要我们添加额外的配置才能实现。在Maven中可以通过插件的方式完成上述的内容。插件的内容我们后续的内容中在详细的介绍,我们先简单了解如何使用。具体配置如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
上面有一点我们要特别注意就是mainClass
参数,我们要指定的是Mavn方法所在类的名称。并且不需要写类的后缀。当我们在pom.xml中配置了上面的信息后,然后执行打包的命令先生成新的打包文件。然后我们进入target目录中执行以下命令运行我的Jar包。
java -jar mazhe-maven-1.0.jar
当我们输入上述命令时,就会发现成功输出了我们Main方法的内容了。
这时可能有人会怀疑,如果我们不配置上面的信息,直接打包,然后直接使用上面的命令运行我们Jar包,会显示什么结果呢?
下面我们尝试一下,将上面pom.xml中配置的build配置注释掉,然后测试一下。
- pom.xml
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Maven实战</name>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.35</version>
</dependency>
</dependencies>
<!-- <build>
<finalName>mazhe-maven-1.0</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>Mazhe</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>-->
</project>
下面我们执行打包命令,然后直接使用上面的命令运行我们新打包的Jar包。
我们看上图所示,Jar在运行时抛出了异常,上面内容指的是没有指定Main方法的类,所以Java不知道要运行哪一个。这也就间接说明了,我们上面配置的信息是没有任何问题的。
- 总结
下面我们在简单总结一下我们上面所介绍的Maven命令。上述命令就是Maven中比较常见的命令:
mvn clean compile // 编译项目
mvn clean test // 执行测试用例
mvn clean package // 项目打包
mvn clean install // 安装项目到本地
我们在执行Maven上述的命令时,如果仔细观察就会发现,上述的命令与命令之间是有依赖关系的。也就是在执行mvn clean test
命令之前是会先执行mvn clean compile
命令的,同样的在执行mvn clean package
命令之前是会先执行mvn clean test
命令的,而类似地,mvn clean install
命令之前会执行mvn clean package
命令。
那么Maven为什么要设置这样的规则呢,我们后续的内容中在详细介绍这方面的内容。
Maven如何解决重复造轮子问题
通过上面我们的介绍,我们知道按照上面步骤,知道了如果在项目中使用Maven。虽然比我们自行引入Java时,方便了许多,但还是有一些比较繁琐的问题。例如上面中介绍的那样,我们要手动的创建pom.xml
。还要按照相应Maven的规则创建src/main/java
和src/test/java
等目录。如果只创建一次还好,如果有多个项目都要创建的话,上面的步骤就比较麻烦了。这也就体现不出来Maven便利之处处了。实际上Maven已经考虑到上面的问题了。于是提供了mvn archetype:generate
命令来允许我们使用命令的方式初始化Maven项目的骨架。
下面我们测试一下,使用mvn archetype:generate
。生成Maven项目骨架时,都会自动给我们生成哪些内容。我们首先选择一个空项目,然后执行上面的命令。
输入mvn archetype:generate
命令。
当我们输入完上面的命令后,Maven就会自动运行创建我们想要的项目骨架。但运行中时,需要我们做出人为的选择。
上面提示的2065,指的就是Maven会安装下面的方式创建项目骨架。那为什么Maven提供的了这么多选项呢?这是因为不同的项目,需要生成的项目骨架是不一样的,例如Java Web的项目的和普通Java项目就是不一样的,所以Maven为了兼容这种情况,提供了很多的选择,我们在使用mvn archetype:generate
创建项目时,通常按照默认的方式创建即可,除非你有特殊的创建需求,在选择指定的创建规则。
我们回车后,让Maven继续创建,也就是默认选择了2065创建。
当我们选择后,会提示让我们选择相应的版本,我们通常选择比较稳定的版本即可,也就是不待任何修饰的版本,我们选择最后一个,也就是默认的选项8。
继续后,Maven又提示了,这回没有选择项了,需要我们手动的填入了,也就是groupId
参数,这个参数上面的内容介绍过,项目的基本坐标,不要重复,我们随便写cn-mazhe.maven
然后继续。
Maven又让我们写artifactId
参数,这个参数我们也介绍过,项目的唯一标识,我们写mazhe-maven-quickstart
然后继续。
这时又提示需要我们指定项目的版本了,并且通过上面的提示,我们可以看到,Maven在提示我们版本时,这个版本号的后面添加了SNAPSHOT修饰,这是什么意思呢?
在Maven中后缀添加SNAPSHOT的为快照版本,它本普通的版本在使用上没有任何区别,但是在下载更新时还是有区别的,我们后续的内容中在详细的介绍有关Maven版本的内容,我们继续。
Maven提示我们这个项目的包路径是不是我们上面指定的那个groupId
参数路径。这个我们可以随便修改。
当我们执行完成后,Maven提示了我们项目构建成功了。下面我们用IDEA打开这个项目看一样,Maven都会我们生成哪些内容了。
如上图所示,使用Maven中的mvn archetype:generate
创建项目骨架时,生成了我们使用Maven默认约定的配置。也就是我们上面内容中手动创建的内容。
并且通过观察我们还发现了在pom.xml中还为我们生成了,很多我们没介绍过的标签内容。这里先不用担心,我们后续的内容中,都会详细的介绍。我们下面执行一下上面生成的代码,看一下项目是否能够运行。
当我们执行时,我们发现,IDEA中并没有显示可以运行的按钮,这是什么原因呢。这是因为我们是在一个空的项目中创建了上面的另外的一个项目,所以IDEA并没有将这个项目识别出是一个Maven项目,已经这是我们创建空项目的一个目录。要想解决这个问题,有两种途径,一种是用IDEA,直接打开我们生成的这个项目。还有一个办法,就是如下图所示,我们设置一下。
选中pom.xml文件,然后我们右键选择Add as Maven Project
即可。
这样我们就可以执行上图中自动生成的代码了。