Maven的基础了解与使用
首发日期:2018-11-04
Maven的介绍:
什么是Maven:
- Maven是apache旗下的一个java开源项目。
- Maven是一个项目管理工具,它可以帮助我们管理项目
- 可以使用Maven对java项目进行项目构建(构建包括清理、编译、测试、报告、打包、部署六个过程,你可以认为是一个项目的从代码到完整可运行项目的过程)、依赖管理。
为什么要学习maven?
以一个例子说一下maven的好处,说了它的好处,
1.maven可以帮我们管理依赖。在以往的时候,我们都是手动的导入jar包,然后build path;在导入jar包之前,我们需要去查找有哪些包,需要哪些包,这是一个非常繁杂的问题,如果你不是“老司机”的话,你可能会遗漏某些jar包;而在maven中,我们可以使用pom.xml来声明需要哪些依赖包,然后maven就会根据pom.xml中的信息去获取仓库中的依赖包的引用,这就相当于导入了jar包。而且,maven存储了大量的jar包和大量的依赖规则,所以你可以直接去百度搜索一下某个关键字+maven
,就可以查找到某个框架或工具的依赖信息,直接把这个依赖信息拷贝到pom.xml中,maven就会帮你管理这个依赖。
2.maven可以帮我们进行工程管理。在以往的时候,我们开发一个项目都是在一个工程中开发,但事实上这对多人开发并不友好,多个开发者之间的开发产生了耦合关系,这对整合造成了一些小困扰(但并不是说不好,只是说有更好的手段)。而使用maven之后,可以建立多个工程来组成一个项目,在不同的工程中开发不同的模块,而多个工程之间的关系由maven管理,maven可以帮我们把多个工程组合成一个项目。
安装与配置:
下载:
点击链接,下载Maven:
下载完了之后,直接解压即可,它是免安装的。
配置环境变量
-
配置
JAVA_HOME
环境变量,值是jdk程序根目录【如果已经配置过的,可以省略】 -
将
%JAVA_HOME%\bin
添加到path环境变量中【如果已经配置过的,可以省略】【注意不要弄乱了path的值】 -
配置
MAVEN_HOME
环境变量,值是maven程序根目录 -
将
%MAVEN_HOME%\bin
添加到path环境变量中【注意不要弄乱了path的值】
测试安装结果:
配置了环境变量后,可以在CMD中键入mvn -v
来测试是否可以运行,下面是我的输出结果:
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00)
Maven home: J:\software\apache-maven-3.5.4\bin\..
Java version: 1.8.0_91, vendor: Oracle Corporation, runtime: E:\SOFTWARD\Java\jdk1.8.0_91\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
Maven概念:
在学会maven之前,先了解几个概念。下面的概念了解即可,后面会在实际的配置中涉及。
坐标
在以往的build path过程中,其实只是把jar包的路径引用添加到eclipse中,eclipse知道需要的包的路径之后,在编译的时候会去引用。
而maven可以根据坐标来指定使用哪个依赖包,坐标经过一定的组合规则就可以得出依赖包所在的路径,maven会根据坐标对应的路径在仓库中来查找jar包。
-
maven用三个向量来组成一个坐标,这个坐标确定唯一的依赖包:
- groupid:公司或组织域名倒序+项目名
- artifactid:模块名\工程名
- version:jar包版本
-
maven会根据坐标在仓库中来查找jar包,把三个向量拼接起来就是依赖包的路径。
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.0.RELEASE</version> </dependency> <!--对应的路径转换格式:groupid/artifactid/artifactid+version+.jar --> <!--org/springframework/spring-core/spring-core4.3.0.RELEASE .jar-->
-
仓库
在上面提到了坐标,你应该认识到了坐标标识了依赖包的路径,而这个路径是相对于仓库的路径的。为什么需要一个仓库呢?上面说了eclipse实质上也是通过路径来导入依赖包的,如果你没有一个统一的文件夹来管理依赖包的话,零散的依赖包足以让你头疼了。而maven的仓库是一个包含了众多依赖包的仓库,而且maven的依赖管理可以很方便地添加依赖包信息。
- 仓库的分类
- 本地仓库:本机上的仓库,通常都会有一个本机仓库,不然你老是需要从远程仓库获取的话就很费时间了;而事实上maven会指定一个默认的本地仓库路径,第一次使用maven的时候,会把maven需要的插件和远程库中存储的jar包下载下来并存储到本地仓库中。
- 远程仓库:事实上你可以给本地仓库存储你自己的jar包,而某个jar包本地没有的时候,就需要从远程仓库下载下来。
- 私服仓库:是一个远程仓库,但距离比较近,通常是企业中自己内部的maven仓库,通常私服maven仓库都会使用nexus来搭建。
- 中央仓库:是maven维护的远程仓库,它里面存储的东西比较全。
- 依赖包的查找顺序:先从本地仓库查找,本地仓库没有就去远程仓库查找(企业中一般是本地仓库->私服仓库->中央仓库)。当查找到了之后,都会下载到本地仓库。【记住初始情况下,maven的仓库是比较干净的,仅仅包含一些基本插件。】
- 仓库中存储的内容:
- 插件【maven的功能依赖于插件,没有插件就没有功能,好比电脑需要驱动,maven的构建是通过插件来实现的】
- jar包
- 自己开发的maven工程【其实你应该知道自己是可以把自己的工程打成jar包,然后可以在别的工程中导入这个jar包来使用这个jar包中的类,所以你是可以把自己的类库放到仓库中的】
入门示例
下面以创建一个maven项目,并使用maven的依赖管理来给项目添加依赖为例:
创建maven工程:
关于如何创建一个maven项目,下面以eclipse中的为例,如果你需要idea的,可以点击下面的链接学习。
图解在IntelliJ IDEA中创建第一个Maven项目
新建工程->选择maven project
添加依赖
在pom.xml中添加下列代码:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
可以看得出来,依赖已经导入了我们项目的类库。
所以我们利用依赖包中的类来编写代码的时候就不会报错了。
Maven标准目录结构
maven要依据一定的目录规范来管理项目,所以要想用maven管理项目,要遵循规范。下面会讲一下目录定义规范,这样是面向全手动创建maven项目的时候,在很多时候,都会使用工具来创建,比如eclipse中就可以直接创建一个maven工程,这个工程遵循maven工程的目录规范。
约定的目录结构
maven构建一个工程要遵循这个目录规范。
- 工程目录【下面的缩进程度代表目录级数】
- src目录:存放工程源码的地方
- main目录:存放主程序的源代码,这里放的是主体程序代码。
- java目录:存放java源文件
- resource目录:存放框架或其他工具的配置文件
- 【为web项目时会多出一个webapp目录】webapp目录:存放页面素材
- test目录:存放测试程序的源代码的地方,这里放的都是一些测试用的代码。
- java目录:存放java源文件
- resource目录:存放框架或其他工具的配置文件
- main目录:存放主程序的源代码,这里放的是主体程序代码。
- pom.xml文件:这个文件定义了这个maven工程的相关配置。
- src目录:存放工程源码的地方
补充:
- maven使用一个pom.xml文件来管理依赖。pom中配置依赖信息和maven的一些配置信息,这会在下面讲解。
pom配置:
- pom是Project Object Model项目对象模型的意思。
- pom.xml是maven工程的核心文件,与构建工程相关的一切配置(依赖信息、插件信息等等)都在这个文件中配置
- pom.xml中的可配置标签很多,我们这里仅仅介绍经常使用的。这些经常使用的也足够你日常使用了。
常见标签
- modelVersion:是pom的版本,一般都是默认生成的,不需要关心
- groupId:标识当前工程的所属组织,写法类似包名。【为什么工程也有这个呢?因为我们的工程也是能打包到本地仓库的。】
- artifactId:当前工程的工程名
- version标签:当前工程的版本号
- dependencies标签:用于管理这个工程的依赖
- dependency标签:指定某个依赖的信息
- groupId:标识依赖包的所属组织,写法类似包名。
- artifactId:依赖包的包名。
- version:依赖包的版本。
- scope标签:依赖包的依赖范围。
- dependency标签:指定某个依赖的信息
依赖级别\依赖范围
依赖信息中的scope标签是用来定义依赖范围的。依赖范围影响依赖的生存周期(就好比游戏中的特殊状态,可能一些状态可用于pk,一些状态可用于打副本,一些状态可用于任务,不同的地方影响效果是否生效。),依赖范围的值有以下几个:
- compile:编译级别的依赖会用在编译、测试、运行的过程中。默认依赖范围:compile。
- test:test仅仅针对test目录下的源文件,由于test目录下存放的是test文件,测试文件一般都不会编译到主程序中,所以test级别的依赖是不会在编译和运行中生效的。只在测试编译和测试运行阶段可用。这个级别的依赖包例如有junit包,junit仅仅用来测试,在主程序编译和运行中是不需要它的。
- provided:provided代表已提供的意思,provided范围会用来编译和测试,不会出现在运行中,provided主要用来处理容器也提供了我们所需要的包的情况。例如servlet-api这种依赖包tomcat容器也会提供给部署的程序,如果提供多个同名的依赖包可能会导致主程序运行出错。
- runtime:在编译的时候不需要(没有用到具体类),但是在运行和测试的时候需要用到,就可以使用这个。例如数据库驱动包,它仅仅在运行和测试的时候会使用到这个依赖包。
- system:类似provided,对于编译和测试有效,运行时不需要,不建议使用,所以不讲。
依赖传递的导入
要注意,在一个工程中,如果导入的依赖需要一系列依赖(A包需要B包才能正常,那么也会导入B包),那么也会导入这一些依赖,例如struts2-core依赖一系列的包。
演示:基于eclipse
仅仅导入struts2-core的时候,maven会自动导入struts2-core所依赖的包:
发现maven自动导入了struts2-core依赖的包(这些依赖包是由struts2-core项目中的pom.xml指定的)
依赖传递问题
- 依赖传递的过程中:
- 路径最短者优先
- A依赖B,B依赖C,C依赖D(版本1.1)
- A依赖E,E依赖D(版本1.2)
- 导入时选择版本D(版本1.2)
- 路径相同,先声明者优先【意思是如果A同时依赖于B和C,而B和C都含有同一个依赖包P,如果在A的继承声明中先声明B,那么A会继承B的依赖包P,所以P版本取决于B】
- 路径最短者优先
依赖排除
可能会遇到一种情况,在同一个工程中,A包依赖B包的1.0版本,C包依赖B包的1.5版本。那么B的1.0和1.5都被引入了。这可能会导致紊乱,因为构建的时候会不清楚使用哪个版本的B依赖包。这时候我们需要使用依赖排除来解决这个问题。【一般都是排除低版本】
例如struts2-core依赖javassist包,hibernate-core也依赖javassist包,根据依赖的依赖的导入规则,这时候会有两个javassist
为了避免紊乱,我们要使用exclusions排除。在eclipse中,我们可以在Dependency Hierarchy中右键选中包进行排除,也可以使用以下xml式排除:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.34</version>
<exclusions>
<!-- 排除struts2-core的依赖中的javassist依赖 -->
<exclusion>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖的继承
在建立多个有继承关系的工程时,父工程的依赖信息会传递给子工程。
在父工程中声明了依赖后,就算子工程没有声明,也会传递到子工程中
版本锁定
在分模块开发时,多个子模块可能会使用上同一个依赖包,那么如何限定多个子模块都使用同一版本的依赖包呢?可以使用版本锁定。
- 在父工程使用dependencyManagement来管理依赖(dependencyManagement不会导入依赖),那么依赖的版本会被父工程锁定。
- 通常情况下,父工程的依赖会传递给子工程(如果你另外也使用了dependencies标签来声明依赖),那么子工程不声明的时候是没问题的;如果子工程显式导入被锁定版本的依赖时,会报错,会提示版本被父工程锁定。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
统一管理依赖版本:
你应该经常见到某个框架的依赖包都是同一版本的,如果我们将这些依赖信息都添加到pom.xml中,可能会出现多个重复的version标签,如果你要修改的话是很麻烦的,要逐个逐个去修改版本。
事实上,是可以统一管理依赖版本的,同一管理依赖版本之后,如果我们要修改这个框架下的包的版本,那么只需要修改一处就行了。
- 使用properties内使用自定义标签统一版本号,自定义标签相当于一个变量,后面的version那里可以引用这个变量。
<properties>
<!-- 使用自定义标签统一版本号,标签名是自定义的 -->
<struts2-version>2.3.34</struts2-version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<!--在version标签中引用 -->
<version>${struts2-version}</version>
</dependency>
</dependencies>
注意:properties标签并不是只能用于统一声明依赖版本,但凡是需要统一声明的地方都可以考虑使用properties标签。
Maven命令
全局命令
- maven -v :查看版本
工程级命令
以下命令针对于maven工程,必须到pom.xml目录才能执行命令
- mvn clean:清理之前编译出的class文件,相当于清理target目录及其目录下的所有文件。
- mvn compile:编译主程序,效果是把src/main/java下的java源文件编译为class文件并输出到target下的classes目录下。
- mvn test-compile:编译测试程序
- mvn test:执行src/test/java下的单元测试类,会打印出测试结果(如果被junit标识成测试类的话),并把src/main/java下的java源文件编译为class文件并输出到target下的test-classes目录下。
- mvn package:打包,java项目打包成jar包,web项目打包成war包。打包的文件存储到target目录下。
- mvn install:安装,把maven工程打包成jar包或war包并保存到本地仓库中。【在本地仓库目录下根据坐标来生成包路径来保存】。对于我们自己的maven工程,可以使用mvn install把它安装到仓库中。
命令生命周期
- 属于同一个生命周期的指令,当后面的命令执行时,前面的命令也会自动执行。
- 常见命令周期:
- clean :在进行真正构建之前的一些清理工作。
- 包含命令:pre-clean,clean,post-clean
- default:构建的核心部分:编译、测试、打包、部署等等。【要注意,大部分命令都处于default生命周期,所以执行某个命令时,生命周期前面的命令也会自动执行。】
- 包含命令:validate,compile,test,package,verify,install,deploy....
- site:生成项目报告、站点、发布站点。。
- 包含命令:pre-site,site,post-site,site-deploy
- clean :在进行真正构建之前的一些清理工作。
每一个阶段都有一个对应的命令,且有相应的插件来支持命令的运行。
setting.xml配置文件
仓库配置
本地仓库配置
maven安装路径下\conf\settings.xml文件,在settings.xml中配置localResposity标签。,它是被注释了的,有配置方法的提示。
默认目录:用户目录下.m2/repository
添加远程仓库
<mirrors>
<!-- 中央仓库1 -->
<mirror>
<id>repo1</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://repo1.maven.org/maven2/</url>
</mirror>
<!-- 中央仓库2 -->
<mirror>
<id>repo2</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://repo2.maven.org/maven2/</url>
</mirror>
</mirrors>
Eclipse中使用maven
配置maven
eclipse有自带的maven插件,但不建议使用。
把独立的maven添加成一个新的maven:
配置user settings:
创建maven工程:
【maven model用于分模块开发时创建子工程,多个子工程与核心工程组成一个完整的项目。】
新建工程->选择maven project
下面给的是打包方式为war的时候的目录结构:
修改JRE版本:
默认情况下,maven工程引用的jre库是1.5的,这可能会导致一些语法问题(例如泛型的语法问题),所以通常需要更改这个引用的jre库版本。
- 修改方法:在setting.xml中修改profiles标签
<profile>
<id>jdk18</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<maven.compiler.encoding>utf-8</maven.compiler.encoding>
</properties>
</profile>
导入依赖
-
方式一:手动添加,通过手动拷贝等方式统一增加。可以百度
包名+maven
来查找依赖信息的写法,拷贝下来粘贴到pom.xml中即可。-
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
-
-
方式二:在eclipse,双击打开pom.xml,默认情况下不是xml界面,点击下面的dependencies选项,再点击add,在中间的搜索框输入信息搜索依赖,选择依赖包,最后点击确认。这要求你比较熟悉需要哪些包。
添加插件
有时候我们希望给maven工程添加额外的插件,例如tomcat7:run插件,这个插件能使得我们的工程虚拟地部署到tomcat中。
-
mvn tomcat:run 命令是一个插件,需要添加才能执行这条命令,添加方式:项目右键选择maven,再add plugin,然后搜索tomcat
-
<!--build与dependencies是同一级的 --> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> <configuration> <!-- 端口 --> <port>8080</port> <!-- 访问路径 --> <path>/helloworld</path> <!-- 编码 --> <uriEncoding>utf-8</uriEncoding> </configuration> </plugins> </build>
-
这里仅仅给一个添加插件的示例,其他的插件有兴趣可以自查。
Eclipse中执行maven命令
工程右键->run as ->第一个maven build可以让你选择之前执行过的命令来执行,第二个maven build可以让你输入指定命令来执行(在goals中输入命令,mvn test只需要输入test即可);至于后面的clean,install和test的意思已经显而易见了。
创建web工程
如果想创建一个web工程,需要在创建工程的时候打包方式改成war。但默认情况下src/main下是没有webapp目录(存放WEB-INF目录和web.xml的目录)的。
- 生成webapp目录:
- 方式一: WEB-INF目录和web.xml,javaEE tools->Generate Deploy...
- 方式二:手动创建目录和文件
依赖范围冲突问题:
什么是依赖范围冲突呢?这涉及到依赖范围问题,主要是provided问题。以servlet-api依赖为例,这个依赖tomcat会提供给我们部署的项目,但如果我们添加这个依赖的时候选择了compile,那么这个依赖也会带到部署的项目中,使得存在了两个servlet-api依赖。
解决方法:这时候要修改servlet-api依赖的范围,改成provided。
依赖排除问题:
这个在pom配置的依赖排除中讲过了。这里只提一下,不再演示。
如何运行项目
java项目:
- 运行方式一:项目右键->run as->java application
- 由于方式二跟直接到目标目录执行java没什么区别,所以这里不讲,有兴趣可以自查
mvn exec
。
web项目:
- 部署方式一:在添加了插件之后,使用tomcat插件部署:mvn tomcat7:run
- 选择5 Maven build之后,输入命令
tomcat7:run
- 区别:插件的是虚拟环境,会比较快。
- 选择5 Maven build之后,输入命令
- 部署方式二:打成war包之后,把war包拷贝到tomcat目录中。
- 部署方式三:建立服务器之后,把项目添加到tomcat中;或者在pom.xml文件点击右键,直接把项目添加到tomcat中。
工程拆分和聚合
- 在一个比较大的项目开发协作中,如果按照以前的开发的话,是一个工程。这样会有一些问题。拆分、分模块便于测试和开发。不同开发者仅仅面向自己的工程。
- 然后在整合的时候,把工程聚合起来,就能聚合成一个完整的项目了。
工程的拆分
工程的拆分是创建一个核心父工程,然后创建多个子模块的情况。核心父工程一般不会编写什么内容,一般只作为依赖的汇总(让父工程引入共有依赖,子工程单独依赖的单独引入)。
创建父工程:
创建子工程
为了让父工程能管理到子工程,所以在父工程下右键"new-->maven module"
创建完父工程和子模块后,可以在父工程的pom.xml中看到父工程管理到了子模块:
而子模块也指向了父工程:
然后子模块怎么开发呢?它相当于与一个独立的模块,所以你可以像往常一样开发。不过提一下的是,可能会发生service层调用dao层,那么这怎么实现呢?这就需要我们在service层中添加dao层的依赖了,这样service层就可以调用dao层的方法了。
这里提一下工程拆分与框架整合的问题,在以前一个工程中,dao和service都是交给spring去管理的,那么现在
该怎么处理呢?首先,要注意的是,只要我们配置了spring(这里注意,依赖已经导入了),无论是xml还是注解
式,那么spring就能够管理好我们的bean。所以,我们现在其实只需要关心我们的bean是否交给了spring来管理
即可。而这个单个模块的测试可以在test目录下再编写代码来测试。
工程的聚合
拆分工程之后,开发完成了,怎么聚合使用呢?
首先,要注意父工程管理着子模块,
如果打包父工程,那么子工程也会进行打包。
而通常我们在进行web开发的时候,由于子模块web是核心项目,而子模块web又依赖着子模块service,而在打包的时候依赖的包也会打成jar包,所以打包子模块web的时候,web工程的lib里面是有依赖的兄弟工程的,所以我们可以直接运行web工程即可。【这是运行war包的情况】
我们也可以选择在IDE中直接运行子模块web(在eclipse直接run as),也是能够调用到其他兄弟模块的功能的。
写在最后
这篇关于maven的博文看完了,你应该能对maven的使用有一个基本的了解。
这里给一个常用的查maven的依赖信息的网站:mvnrepository.com