什么叫做Maven
在上一篇中我们简单的介绍了一下 Maven实战这本书的简介以及相关的背景,在这一篇中我们开始详细的介绍一下这本书的内容。今天我们分享的内容主要是详细的介绍一下Maven的基本信息。例如什么叫做Maven,为什么我们需要Maven,以及如何安装它。
首先我们从英语的角度看什么是Maven。在英语的角度上看Maven可以翻译为“内行、专家”。所以当我们以后看到这个单词的时候,它除了是一项技术外,我们还学习了一个新的单词的含义。那么在技术领域的角度上看。Maven实际上是一个跨平台的项目管理工具。说是项目管理工具,是因为它支持项目的的构建、编译、打包和依赖管理等内容。(如果不理解上面说的构建、编译、打包是什么意思,也不用担心,后面的章节中将详细介绍)。说它夸平台是因为Maven是由Java语言开发的。所以在使用Maven的时候,必须安装JDK才可以使用。并且Maven也是Apache组织中的开源的项目。为什么要强调这一点呢,是因为如果一个项目是Apache的项目,大概率这样的开源项目流行的概率是比较大的。总之简单概括一下,Maven是一个跨平台的项目管理工具,它支持项目的构建、编译、打包和依赖管理等内容。
为什么需要Maven
介绍了这么多Maven的好处,那么我们在日常的开发中为什么需要Maven呢?在没有Maven之前我们如何开发呢。下面我们带着这个问题,看一下下面的例子,例子很简单,我们使用工具类将对象转成Json字符串并输出。并且先不使用Maven的情况下实现上面的内容呢。实现上面的方式的工具类比较多fastjson的方式实现。
我们首先创建一个普通的Java项目,具体操作如下:
我们简单输出一句话,验证一下项目是否有问题。
执行上面的代码:
Hello World Mazhe Maven!
下面我们创建一个Userinfo对象,然后输出对象的属性。具体操作如下:
- 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', password='maven'}
下面我们引入fastjson的依赖包,由于我们不使用Maven,所以我们使用下面的方式引用,具体操作如下:
我们首先在项目的根目录中创建一个目录来存放我们引入外部的依赖包。当然我也可以不在项目的根目录中创建,在任何目录中引入都可以,只不过在项目中创建的好处是为了避免因目录改动,导致我们之前引入的依赖包找到不相应的目录,而导致引入的依赖包不可用。
然后我们将我们需要要依赖包拷贝到我们刚刚创建的目录中。如果我们这前配置过,那么这个依赖包就可以直接在项目中引入了,如果我们没有问题过,那么项目中还是使用不了我们这个依赖包的,因为它没有识别出这个目录。那以怎么判断项目是否识别同这个目录呢?很简单我们只需要看一下,这个目录中的依赖包,是否可以直接查看相应的源码即可。如果项目中显示了依赖包的目录结构,并且可以直接查看源码,那么则证明我们项目识别出了这个依赖包,可以直接在项目中引用了。反之亦然。
我们通过上图观察到,项目并没有显示出依赖包的目录结构,所以也就说明项目并没有识别出这个依赖包。下面我们通过下面的配置即可。配置的方式有很多,我们采用下面的方式实现。
我们在IDEA的File菜单中选择Project Structure ...
然后选择Libraires
点击+
选择Java
然后选择我们之前项目中创建的目录即可。
这块就是我们上面所说的,我们实际上也可以不选择一个目录,而是直接选择一个依赖包,这样我们依然可以在项目中引入它。只不过选择目录好好处是,如果我有新和依赖包的要引入时,直接拷贝到这个目录中就可以直接引入了,而不需要在次配置了。这就是选择目录的好处。
我们在次查看项目中的依赖包,发现项目中可以识别出我们依赖包的里的目录结构了。下面我们修改一下代码来将我们之前说的把对象转成Json。
- 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));
}
}
- 输出
/Users/md/IdeaProjects/md-mazhe-maven/src/Mazhe.java:8:27
java: 无法访问com.alibaba.fastjson2.util.Wrapper
找不到com.alibaba.fastjson2.util.Wrapper的类文件
当我们以为大功告成的时候,发现上面的代码居然报错了。我们看上面的输出日志发现是缺少了fastjson2
的依赖包。下面我们将这个依赖我也拷贝到项目中。
我们在次执行上面的代码,查看一下日志输出。
- 输出
Exception in thread "main" java.lang.NoClassDefFoundError: com/alibaba/fastjson2/support/AwtRederModule
at com.alibaba.fastjson.JSON.<clinit>(JSON.java:84)
at Mazhe.main(Mazhe.java:8)
Caused by: java.lang.ClassNotFoundException: com.alibaba.fastjson2.support.AwtRederModule
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 2 more
我们发现日志还是报错,并且从日志中我们找到了java.lang.ClassNotFoundException
异常的关键字,说明我们依赖包里还缺少依赖。下面我们将缺少的依赖在拷贝到项目中。
在次执行上面的代码,观察日志输出。
- 输出
{"passworld":"maven","username":"mazhe"}
我们看日志输出发现这次项目执行成功了,我们成功的将对象转换成Json了。但这时可能有人会有些疑惑,上面的报错,为什么你能知道缺少的是哪个依赖包?这实际上也就是我们在没有使用Maven管理项目一个非常耗费时间的一个工作。因为这个是需要一点一点尝试出来的。尝试的方式,可以是直接看项目的源码,看一下这个源码中依赖的包是哪一个。或者简单一点将相同的依赖包全都拷贝到项目中,但这在实际的项目开发中是不推荐的。
除了这个问题外,还有的问题就是依赖包与依赖包冲突,因为现在依赖包的版本非常的多,通常都有一个稳定的依赖版本,如果依赖版本引入的不正确,也会导致相应的依赖包不可用。这也就是为什么我们在使用Maven的管理项目依赖的好处,它可以大在提高我们的工作效率。例如作者在书中所举的例子一样,例如我们要买一台电脑,我们现在方式相当于我们自己组装一台电脑,那么这样的工作是非常繁琐的,并且需要我们非常了解显卡、内存、主板等这方面的内容。除了这个入门比较难之外,还有一个问题是,可能会因为我们选择的配置有问题,导致不兼容最后导致电脑不可用。但这么做也有好处,就是可以让我们非常详细了解这方面的知识。那使用Maven的方式和这个方式而完全不同,相当于,我们购买了一台笔记本。它不需要我们了解上面的内容,我们只是需要选择好我们想要购买的牌子即可。这两种的效率可是截然不同的。这也就是使用Maven的好处。
和Maven很像的东东
下面我们了解一下,在没有Maven之前,那我们除了上面说的方式,还有哪些其它的项目管理工具可以提高我们开发效率吗?实际上也是有的,并且不只一个,下面我们分开介绍一下。
- Make
Make几乎可以理解为最早的项目构建工具。它由Stuart Feldman于1977年在Bell实验室创建的。在使用Make时需要定义一个名为Makefile的脚本,Make通过该脚本来达到项目构建的目的。其它以后的项目构建工具也采取了同样的方式,例如Maven中的pom.xml
文件。该脚本使用了Make自己定义的语法格式。语法中包括一部分规则(Rules),而规则中又包括目标(Target)、依赖(Prerequisite)和命令(Command)等信息。
下面是具体的Make的语法如下:
# 指定编译Java代码时使用的参数
JFLAGS = -g
# 指定Java编译器
JC = javac
# 指定生成Jar包时使用的参数
JARFLAGS = cvfm
# 指定生成的Jar包文件名
JARFILE = MyProgram.jar
# 定义一种依赖关系,指示如何从.java文件生成.class文件
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) $*.java
# 定义要编译的Java源代码文件列表
CLASSES = \
MyClass1.java \
MyClass2.java \
MyClass3.java
# 定义默认规则,指示执行`jar`规则
default: jar
# 定义`classes`规则,指示如何编译Java源代码
classes: $(CLASSES:.java=.class)
# 定义`jar`规则,指示如何生成Jar包
jar: classes
jar $(JARFLAGS) $(JARFILE) Manifest.txt $(CLASSES:.java=.class)
# 定义`clean`规则,指示如何清理生成的class文件和Jar包文件
clean:
$(RM) *.class $(JARFILE)
上面的内容就是Make的语法规则,由于此项目管理工具太早了,所以我们只简单了解一下即可。下面我们详细介绍一下上面具体的规则。
- default:默认规则,指定执行jar规则。
- classes:编译Java源代码生成class文件。
- jar:生成Jar包。
- clean:清理生成的class文件和Jar包文件。
要生成Jar包,只需在终端窗口中输入make jar命令即可。该命令会执行Makefile文件中定义的jar规则,生成Jar包。如果需要清理生成的class文件和Jar包文件,可以输入make clean命令。
- Ant
在Make之后,Ant出现了。为什么会出现Ant呢?这是因为Make不支持跨平台,这对于使用Java来说是不友好的,并且Make的语法也比较难,通过编译失败都是因为多少空格导致的,但这很难提前发现问题。在Ant中使用了build.xml作为项目的构建脚本。这点和Maven比较像。相比Makefile的语法Ant的xml方式,则要方便很多。当然也有其它的构建工具,例如Gradle,它是采用Groovy编程语言来定义构建脚本的,相比xml来说,又方便了很多,所以Gradle也被移为Maven之后,下一代的构建工具。
下面我们介绍一下Ant构建脚本的具体配:
<?xml version="1.0"?>
<project name="MyProject" default="jar">
<!-- 定义项目的属性 -->
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="dist.dir" value="dist"/>
<property name="main.class" value="com.example.MyClass1"/>
<!-- 创建目标目录 -->
<target name="init">
<mkdir dir="${build.dir}"/>
<mkdir dir="${dist.dir}"/>
</target>
<!-- 编译Java源代码 -->
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${build.dir}"/>
</target>
<!-- 生成Jar包 -->
<target name="jar" depends="compile">
<jar destfile="${dist.dir}/${ant.project.name}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
</target>
<!-- 清理生成的文件 -->
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
</target>
</project>
我们通过上面的配置,第一次直观的感受就是,Ant的构建配置确实比Makefile的语法相对来说比较简洁。下面我们同样的详细介绍一下Ant的配置规则:
> init:创建编译和生成Jar包所需的目录。
> compile:编译Java源代码。
> jar:生成Jar包。
> clean:清理生成的文件。
要生成Jar包,只需在终端窗口中输入ant jar命令即可。该命令会执行build.xml文件中定义的jar任务,生成Jar包。如果需要清理生成的文件,可以输入ant clean命令。
注意:在执行该build.xml文件之前,需要将文件中的属性值替换为您的项目属性。例如,src.dir属性指向包含Java源代码的目录,main.class属性指向包含main方法的类的类名,这将是您的程序的入口点。
通过上面的介绍我们发了一个问题,那就是不管是Make还是Ant虽然都提供了自动化管理项目的功能,但都是正对项目打包和编译的功能,并没有提供项目的依赖管理,也就是我们依赖包的管理,实际上项目开发中,这项的工作实际是最繁琐的。所以Maven的出现改变了上述的工作方式,除了上面的内容外,Maven还支持依赖管理、项目管理等内容,并且和Ant相比,Maven定义了很多额外的约定了,只要遵循这些约定,就可以很方便的管理项目。但Ant而不是,所以它也是用xml做为构建管理的,但并没有过多的约定。所以常常需要人为的约定,否则,在不同的环境中的Ant构建脚本,在换到其它环境中,则会因为目录或者其它的原因导致编译失败。
Maven有哪些缺点
在开始介绍Maven之前,我们先介绍一下Maven的缺点,任何技术都有它的适用之处,只有我们采用是了合适的技术,才是这项技术最佳的使用方法。
- Maven过于复杂
说Maven复杂是相比来说的,因为它几乎帮我们处理了项目管理的全部工作,例如清理、编译、测试、打包、发布等等。但要想实现上述说的实现,难度是可想面知的,所以可以说Maven的入门还是比较难的。
因为Maven是用来管理项目的,清理、编译、测试、打包、发布,以及一些自定义的过程都是一件非常复杂的事,所以可以说的Maven入门还是比较难的。
- Maven的仓库十分混乱
我们知道通过Maven是可以管理我们的依赖包的,因为Maven支持下载的功能,但有时会因为一些原因导致,我们下载失败,这时我们只能手动的去下载。并且Maven为了我们方便管理依赖包,提供了中央仓库的概念,但Maven的中央仓库并不完美,你们常常会发现相同的依赖包在两个不同的路径中。
- Maven官方文档非常凌乱
我们知道作为一个开源的项目工具,官方的文档是很重要的,它是我们第一时间了解一项开源技术的唯一途经。其中有一些开源项目的管方文档确实写的比较好,例如Spring的相关文档。当有一些开源项目文档写的则比较差,其中Maven就是其中之一。Maven的官方网站的文档非常凌乱,各种插件的文档寻找非常吃力。通过官方文档,很难入门。
Maven的安装
介绍了这么多,我们终于来到了这一步,也就是安装,任何技术都是从安装开始的。那么Maven也是一样的,不同的地方在于,Maven在装之前需要有些额外的配置。
- 安装前环境的准备
由于Maven是使用Java语言开发的,所以在使用Maven之前需要提前把Java的环境安装好,也就是需要检查是否成功的安装了JDK。具体执行的命令如下:
java -version
- 输出
这样则表示我们的环境变量配置成功了。
如果本地成功的安装JDK,则会直接显示Java的版本信息。如果想查询本地JDK的安装路径可以执行下面的命令查询:
echo $JAVA_HOME
- 下载Maven
下面我介绍一下如何下载Maven。和其它开源软件一下,软件一定是要通过官方网站下载的。下面是Maven官方网站的地址:
当我们访问上述官方网站时,首页直接就会看到Maven的下载地址。我们直接点击Download链接即可:
当我们点击后就会看到有多种类型的安装包供我们下载,也就是如下图所示:
它们具体的区别是,提供了不够平台的下载文件。如果是Linux或者是MacOS推荐使用tar.gz
的下载文件,如果是Windows平台的推荐使用zip
后缀的下载文件。除此之外,Maven官方还提供了,上述不同平台的md5校验和(checksum)文件和asc数字签名文件,目的是可以用来检验Maven下载包的正确性和安全性。如果你想对Maven的源码感兴趣可以下载src
的版本文件,自己构建Maven。那由于我是MacOS系统,所以我下载tar包就可以了。
- 安装Maven
当我们下载完对应的平台的版本后,我们先直接解压然。但解压后我们还不能直接用Maven。我们需要配置Maven的环境变量之后在使用。由于不同平台的配置方式有所不同,我们主要介绍两种平台的配置。我们首先介绍一下如何在Windows平台中配置Maven的环境变量:
- 我们首先需要在系统的环境变量中新创建一个
M2_HOME
的变量,路径为上面Maven解压后的全路径,注意结尾不要加任何符号。 - 接着在系统中环境变量中查找到
Path
变量,然后在改变量的后面追加下面的配置:%M2_HOME%\bin;
注意后面的分号。如果配置完成后,可以在命令窗口中查询一下配置的
M2_HOME
的变量是否配置成功。echo%M2_HOME%
如果该命令输入的结果是我们Maven下载后解压的路径,则表示我们配置成功了。接着我们可以输入下面的命令来验证Maven是否安装成功。
`mvn-v`
如果此命令成功的显示了Maven的安装版本则表示,我们Maven配置的成功了。
下面我们看了解一下如何在Linux或者MacOS系统中配置Maven环境变量。首先我们在命令窗口中执行下面的命令:
sudo vim ~/.bash_profile
当输入上述命令后,系统会提未你输入机器的登陆密码,当我们输入成功后,就会进入到文件当中,然后将下面的配置添加到bash_profile
文件中。
export MAVEN_HOME=/Users/jilinwula/Downloads/apache-maven-3.6.3
export PATH=$PATH:$MAVEN_HOME/bin
注意,上面说的MAVEN_HOME
对应的是系统的Maven解压后的路径。当设置完成后,输入:wq
命令保存我们刚刚打开的文件。然后我们还需要执行下面的命令,让上述的配置立即生效。
source ~/.bash_profile
由于我们已经输入完机器的登陆密码了,所以这次我们不需要在输入机器的密码了。
接着,我们输入mvn-v
命令,看检查是否成功的返回的Maven的版本信息。
看上图中显示了我们指定的Maven版本,并且还显示出了我们Maven配置的路径,这表示我们Maven配置成功了,可以直接使用了。
Maven的目录结构
下面我们介绍一下Maven安装包的目录结构,通过目录结构可以让我们更加理解和掌握Maven。在Maven中主要包括下面的目录和文件下面我们详细介绍一下:
- bin
很多的开源软件中都包括这具目录也就是目录bin
目录。在这个目录中主要包括了Maven运行的所有脚本文件,其中没有带.cmd
后缀的命令为Linux平台的脚本文件,带.cmd
后缀的为Windows平台的脚本文件。在此目录中还提供了我们上述中我们介绍过的mvn
命令脚本。除此之外Maven还提供了mvnDebug
命令。它俩的区别是后者在运行时开启debug模式,可以允许我们调试Maven。下图为bin
目录的相关截图:
- boot
boot
目录下有一个jar文件。该文件的作用是Maven自己实现的类加载器。和默认的Java类加载器相比,它提供了丰富的语法和配置,Maven使用此类加载器加载自己的类库。
- conf
conf
目录中包含了一个非常重要的文件settings.xml
文件。在后续的内容中我们会详细settings.xml
文件。通过该文件我们可以全局地定制Maven的行为。
- lib
lib
目录中包括了Maven运行时依赖的所有的Java类库。这也间接的说明了Maven是使用Jave语言开发的。