每个程序员需知道的Maven知识

每个程序员需知道的Maven知识

一、以前的日子:
没有Maven 的时候,jar包默认放在 lib的目录下,然后把该目录设置为 classpath可以读取到的目录,如下图:
在这里插入图片描述
某一天我们新加了一个功能,需要用到一个比较古老的.jar包,通过网上各种搜索终于在历经重重阻碍后在某个网站找到了。然后把z.jar下载到本地,并拷贝到/lib目录下。
在这里插入图片描述
这时运行项目抱一堆错,原因是z.jar包有很多依赖项,分别是z1.jar,z2.jar,z3.jar。这时tmd 想骂人,但还是要去找这些依赖项。没办法自己挖的坑怎么样也要填完。于是又经历种种障碍,终于将这些依赖jar包都找到了,下载本地并拷贝到/lib目录下。所有的依赖jar包都拷完后,项目终于能运行成功了。
过了半个月,领导又说了要加一个功能,需要的jar包又是很罕见的,这是又要升级打怪,直到项目运行成功…

二、现在的日子
随着越来越多的开发工具出现,也进一步解放了程序猿的生产力。例如:SpringBoot,Maven,Git,Docker等,而Maven的出现很好的解决了jar的问题。
我们都知道一个项目,需要依赖各种各样的jar包,如果我们用手工的方式 一个一个去管理维护的话,我们会迷失在jar包海洋里的,这是通过Maven这种管理jar包的工具来帮助我们解决繁琐又棘手的问题,可以让我们专心于自己的功能与业务。
Maven 是一套软件工程管理和整合工具。包括很多功能,常见的有:

  • 工程的创建、构建、测试
  • 依赖的管理
  • 仓库的管理
  • 自动化部署

有了Maven 之后,我们:

  • 不需要为每个项目创建一个/lib目录用来存放各种jar包了
  • 不需要为到哪里去寻找我需要的jar包而发愁了
  • 不需要为引用的jar包去寻找他所依赖的jar包了

三、Maven 结构
典型的Maven 结构图:
在这里插入图片描述
四、仓库:
仓库:
在Maven 术语中,仓库是一个位置(place),例如目录,可以存储所有的工程jar文件、library jar文件、插件或任何其他的工程指定的文件。
Maven只有两种类型的仓库:

  • 本地 (local)
  • 远程 (remote)

本地仓库:
Maven 的本地仓库是机器上的一个文件夹。在第一次运行任何maven 命令的时候创建。
Maven本地仓库保存工程的所有依赖(library jar、plugin jar 等),当运行一次maven 构建时,Maven会自动下载所有依赖的jar文件到本地仓库中。避免了每次构建都引用存放在远程仓库上的依赖文件。
Maven 的本地仓库默认被创建在${user.home}/.m2.repository目录下。在settings.xml可以重新定义文件的位置。

注:
settings.xml是maven的全局配置文件
而pom.xml文件是所在项目的局部配置。
Settings.xml中包含类似本地仓储位置、修改远程仓储服务器、认证信息等配置。
配置优先级:
局部配置优先于全局配置。
配置优先级从高到低:pom.xml> user settings > global settings

远程仓库:
Maven 的远程仓库可以是任何类型的存储库,可通过各种协议,例如file://和http://来访问。
这些存储库可以是由第三方提供的可供下载的远程仓库,例如Maven的中央仓库(center repository):uk.maven.org/maven2;repo.maven.apache.org.maven2;
也可以是在公司内的FTP服务器或者HTTP服务器上设置的内部存储库,用于在开发团队和发布之间共享的artifacts。

中央仓库:存储了互联网上的jar
Maven 的中央仓库是Maven 社区维护的,里面包含了大量常用的库,我们可以直接引用,前提是我们的项目能访问外网。

私有仓库(私服):公司内部局域网的一台服务器而已
通常都是企业内部创建的一个私有库,用于内部jar包的维护和共享。由于网络原因和鉴于安全性的考虑,多数公司的内外网是隔离的,要想直接访问中央仓库是不可能的,并且直接把内部资源暴露在互联网上也是非常危险的,故就需要创建一个私有库。
私服往往充当了中央仓库的镜像。

仓库之间的关系:
在这里插入图片描述
Maven 项目想要一个jar包的话,他应该从哪一个仓库去获取?
首先Maven 会到本地仓库中去寻找所需要的jar包,如果找不到就会到配置的私有仓库去找,私有仓库也找不到的话就到配置的中央仓库去找,如果还找不到就会报错。但是只要找到了就会返回,除非仓库中有更新的版本或者snapshot版本。

五、最佳实践:
官方并不推荐直接配置远程仓库,例如直接配置中央仓库,而是通过仓库管理器来下载我们所需要的jar包。试想一下,如果你所在的公司有几千个开发人员,每个人都单独配置一个中央仓库,那每个人都到中央仓库去下载所需要的jar,那么就退化成原始模式,且造成巨大资源浪费。
仓库管理器是一种专用服务器应用程序,目的是用来管理二进制组件的存储库。Maven项目的最佳实践。
仓库管理器用途:

  • 充当中央Maven存储库的专用代理服务器
  • 提供存储库作为Maven项目输出的部署目标

使用仓库管理器的优点:

  • 显著减少了远程仓库的下载次数,节省了时间带宽,从而提高了构建性能
  • 减少了对外部存储库的依赖,提高了构建稳定性
  • 与远程SNAPSHOT存储库交互的性能提高
  • 提供了一个有效平台,用于在组织内外交换二进制工件,无需从源代码中构建工件。

六、镜像:
Mirror 相当于一个代理,它会拦截去指定的远程Repository下载构件的请求,然后从自己这里找出构件回送给客户端。配置Mirror的目的一般是出于网速考虑。
Repository:本身是一个仓库,可以对外提供服务
Mirror:不是一个仓库,只是远程仓库的加速器
需要注意的是很多本地仓库搭建工具往往也提供Mirror服务。
栗子:如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。意味着任何一个可以从某个仓库中获得的构件,都可以从它的镜像中获取。
在这里插入图片描述
七、生命周期:
生命周期是由一组顺序阶段构成的一个整体。
一组:指可能有多个
顺序:指按照顺序执行
阶段:指具体要执行的内容
例如:Maven 有三个内置的构建生命周期:
default,clean和site。每个生命周期都由一系列的阶段所构成,比如default生命周期的一个简易阶段如下:
在这里插入图片描述
上图每一个节点都是一个阶段,阶段的执行是按照顺序的,一个阶段执行完成之后才会执行下一个阶段。

八、坐标:
说到Maven坐标,需要想到GAV,即groupId ,artifactId,version。由这三个属性就可以唯一确定一个jar包了。

  • groupId :表示一个团体,可以是公司、组织等
  • artifactId : 表示团体下的某个项目
  • version :表示某个项目的版本号

它们之间的关系是一对多

九、依赖:
依赖:
Maven 的核心特点之一是依赖管理。一旦我们开始处理多模块工程(包括数百个子模块或者子工程)的时候,模块间的依赖关系就变得非常复杂。针对这种情况,Maven提供了一种高度控制的方法。

依赖传递:
栗子:假设B依赖C,当A需要依赖B时,自动获得了对C的依赖。在程序里就是当我们需要依赖很多jar包时,我们可以声明一个包来依赖所有的jar,然后只要依赖我们声明的这个包就可以了。但是可能会造成依赖冲突。

依赖冲突:
同一个项目中由于不同的jar包依赖了相同的jar包,此时就会发生依赖冲突。
在这里插入图片描述
可以看到项目中依赖了a和c,而a和c都依赖b,这时就冲突了。
Maven 采取两种策略来解决冲突:即是短路优先声明优先
短路优先:
指从项目一直到最终依赖的jar的距离,哪个距离短就依赖哪个,距离长的将被忽略掉。
在这里插入图片描述
声明优先:
通过jar包声明的顺序来决定使用哪个,最先声明的jar包总是被选中,后声明的jar包会被忽略,如下图:
在这里插入图片描述
依赖排除:
如果我们只想引用我们直接依赖的jar包,,而不想把间接依赖的jar包也引入的话,那么可以使用依赖排除的方式,将间接引用的jar包排除掉。

解决冲突:
项目中出现冲突,大体都是因为上述所描述的原因,然后Maven在选择jar包时,选择了一个错的包,导致出现问题,这时就需要我们人为来干预下,告诉Maven使用哪个正确的包。
栗子:如果一个类在两个jar中都有,但是某一个方法只在其中一个jar包的类中(a.jar),另外一个没有(b.jar),启动项目报找不到该类的某个方法,猜想可能是maven选错了jarb包。
验证猜想步骤:
1.用Maven指定分别打印出两个jar包的依赖树,看是哪个距离短
2.如果发现距离短的有该方法,那有可能Maven选择的是优先声明规则
3.即b.jar在a.jar之前声明。
4.有2种方法解决此冲突:让a.jar在b.jar之前先声明或者在引用b.jar包的类中将引用b.jar去掉

依赖管理:
聚合:将多个项目同时运行就称聚合。
聚合优点:可以在一个地方编译多个pom文件。ps:聚合时packing必须是pom即pom
继承:类似java的继承,Maven的继承特性也会继承父pom中的依赖。
插件:插件是Maven 的核心,所有执行的操作都是基于插件来完成的。
为了让一个插件中可以实现众多的类似的功能,Maven为插件设定了目标,一个插件中有可能多个目标。其实生命周期中的每个阶段都是由插件的一个具体目标来执行的。
指令
常用的有:
mvn-version:显示版本信息
mvn clean :清理项目生产的临时文件,一般是模块下的target目录
mvn compile :编译源代码
mvn package:项目打包工具,会在模块下的target目录生成jar或war等文件
mvn install :将打包的jar/war文件复制到你的本地仓库中,供其他模块使用
mvn deploy: 将打包的文件发布到远程参考,提供其他人员进行下载依赖
mvn eclipse:eclipse: 将项目转化为Eclipse项目
mvn dependency:tree: 打印出项目的整个依赖树
mvn archetype:generate :创建Maven的普通java项目
mvn tomcat:run:在tomcat容器中运行web应用
mvn jetty:run:调用Jetty插件的Run目标在Jetty Servlet容器中启动web应用

指令参数:很多命令都可以携带参数以执行更精准的任务。
例如:mvn packgae -Dmaven.test.skip=true 以-D开头,将maven.test.skip设为true,表示Maven打包的时候跳过单元测试。

十、Maven 的五大问题

  1. Maven 到底有哪些仓库? 它们是什么关系?
    本地仓库,私服(私有仓库),远程仓库。一对多的关系
    本地仓库:我们开发的时候需要jar包,不可能每次都去互联网上下载吧,多费劲。本地仓库就相当于加了一层jar包缓存,先到这里来查,查不到就去私服上查,再查不到就去中央仓库去找,找到后会把jar包的信息同步到本地仓库和私服中。
    私服:公司内部局域网的一台服务器。试想一下,项目A依赖别人的项目B的接口,怎么做呢?没有Maven的时候,copy项目B jar包到你本地的lib中引入。Maven的方式是,需要其他人把项目B deploy部署到私服仓库中供你使用
    中央仓库:存储了互联网上的jar

  2. 关于的使用
    依赖管理:

    <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.1.1</version>
            </dependency>
    

    这些标签揭示了jar的查找坐标。三者缺一不可,能找到唯一的一个jar
    version分为开发版本(Snapshot)和发布版本(Release)为什么要分呢?
    因为在实际开发过程中,有这样的场景,如果A服务依赖于B服务,A和B同时开发,B在开发过程中发现了bug,修改后,版本由1.0变成了2.0,那么A必须也必须在pom.xml中升级版本,过几天后,B又发现了问题,进行修改后升级版本发布,并通知A进行版本升级…开发过程中版本的不稳定性导致这样问题。
    针对这样的问题,Maven 已经有了解决的方案,就是使用Snapshot版本,在开发过程中B发布的版本标志为Snapshot版本,A依赖的时候选择Snapshot版本,那么每次B发布的话,会在私服形成带时间戳的Snapshot版本,而A构建的时候会自动下载最新的Snapshot版本!

  3. 既然Maven进行了依赖管理,为什么还会出现依赖冲突?处理依赖冲突的手段是什么?

  4. 引入依赖的最佳实践,提前发现问题!
    在工程中,加入了依赖后一般是在运行时才发现存在依赖冲突,然后去解决,似乎有点晚,能 不能提前发现呢?
    当新加入一个依赖后,可以使用mvn dependency:tree 命令,查看是否存在依赖冲突,有的话就提前解决。

  5. Maven的生命周期
    在这里插入图片描述
    注:执行后面的命令,前面的命令自动得到执行
    clean:有问题,多清理!
    package:打包成jar或者war包,会自动执行前面的clean+compoile
    install:将本地工程Jar上传到本地仓库
    deploy:上传到私服

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值