Maven系列3:详解maven解决依赖问题(该系列从 路人甲java 学习)

使用maven创建的项目,有一个重要的文件,就是pom.xml文件。想要用maven帮忙处理项目,我们就要在pom.xml中说明要导入什么包,解决什么依赖,怎么打包,部署,并且项目结构也要按照maven的规定来。

1、约定配置

 使用maven搭建的项目时,提倡使用共同的标准目录结构,maven使用约定优于配置的原则。

 ${basedir}     放置pom.xml和所有的子目录

 ${basedir}/src/main/java   放置项目的java源代码

 ${basedir}/src/main/resources  放置项目的资源文件,例如property文件,springmvc.xml文件

 ${basedir}/src/main/webapp/WEB-INF web应用文件目录,web项目的信息,比如存放web.xml,本地图片,jsp视图页面

 ${basedir}/src/test/java    放置测试代码,例如junit

 ${basedir}/src/test/resources  放置测试用的资源

 ${basedir}/target  打包输出目录

 ${basedir}/target/classes  编译输出目录

 ${basedir}/target/test-classes  测试编译输出目录

 Test.java   maven只会自动运行符合该命名规则的测试类

 ~/.m2/repository  默认的本地仓库目录位置

2、pom文件

 当我们使用maven来解决jar冲突,jar依赖导入,项目的编译,测试,打包,部署时,必须要有pom.xml文件,这些都是在pom文件配置的。

 pom(project object model,项目对象模型),是maven工作的基本工作单元,是一个xml文件,项目运行时,maven到pom中获取配置信息,然后执行目标

 pom中可以指定以下配置:

         项目依赖、插件、执行目标、项目构建、项目版本、开发者名单、相关邮件列表信息

3、maven坐标

 maven引入了坐标的概念,每一个构件都有自己的坐标,我们使用maven创建的项目需要标柱其坐标信息,依赖的其他构件也需要这些构建的坐标信息

 maven中构建坐标是通过一些元素定义的,它们是groupId,artifactId,version,packaging,classifier

     <groupid>定义当前构件所属的组,通常与域名反向一一对应</groupid>

     <artifactId>项目组中构件的编号<artifactId/>                   (一个groupId中可以包含有多个项目,就是通过artifactId来进行区别的)

     <version>当前构件的版本号<version/>

     <packaging>定义该构件的打包方式,默认为jar,可选(jar,war,ear,pom,maven-plugin)<packaging/>

其中前三个是必须要定义的

 例如:

  <groupId>com.javacode2018</groupId>
  <artifactId>springboot-chat01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

我们可以将创建的springboot项目发布出去,然后只需要告诉别人springboot这个项目的坐标信息,其他人就可以直接使用了,而不用知道该项目都依赖了什么

4、maven导入依赖的构件(maven如何引入需要的依赖)

maven可以帮我们引入需要的构件,而maven是如何定位到某个构件上的呢?

当引入第三方jar包时,我们需要知道其坐标信息,然后将这些信息放入到pom文件的dependencies元素中

  <dependencies>

    <dependency>

      <groupId><groupId/>

      <artifactId><artifactId/>

      <version><version/>

      <type><type/>

      <scope><scope/>

      <optional><optional/>

      <exclusions>

        <exclusion><exclusion/>

        <exclusion><exclusion/>

      <exclusions/>

    <dependency/>

  <dependencies/>

在dependencies中,有多个dependency,每一个dependency都是一个依赖的的构件,groupid、artifactid、version这个三个是必须的

type:依赖的构件类型,和packaging对应,默认是jar

scope:构件作用的范围(编译源码,编译测试代码,运行测试,运行项目)

option:标记依赖是否可选

exclusion:用于排除传递性的依赖

5、maven依赖范围(scope)

需求:一个项目引入的构件要让它在一些时期起作用,一些时期不起作用。例如junit,只在编译测试代码,运行测试用例时使用,项目发布后就不再使用

   这需求怎么实现呢?

   java中编译代码、运行代码都需要通过classpath变量,classpath用来列出当前项目需要依赖的jar包,maven需要用到classpath的阶段又4个,编译源代码,编译测试代码,运行测试代码,运行项目,这四个阶段需要的claspath值不一定相同,这个maven中scope可以提供这个支持,scope是用来控制被依赖的构建和classpath的关系(编译,打包,运行所用到的classpath)

   <scope><scope/>标签解决这一个问题:scope可选择的值:

           compile:编译依赖范围,默认值。编译代码,编译测试代码,运行测试用例,运行项目都起作用

      test:测试依赖范围,使用此依赖的话,只对测试代码编译,测试代码运行有用。比如junit

   provide:已提供的依赖范围。表示项目的运行环境中已经提供了所需要的构件,此构件在编译源码,编译测试代码,运行测试代码时都有效,但是在运行时无效。比如servlet-api,在运行时由web容器提供,不需要maven在帮忙引入。

     runtime:运行时依赖范围,使用此依赖范围的maven依赖,对于编译测试代码,运行测试,运行项目时都有效,但是在编译源码是无效。例如jdbc驱动实现

          system:系统依赖范围,该依赖与3中classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示第指定依赖文件的路径。这种依赖直接依赖于本地路径中的构件,可能每个开发者机器中构件的路径不一致,所以如果使用这种写法,你的机器中可能没有问题,别人的机器中就会有问题,所以建议谨慎使用。

如下:

<dependency>
    <groupId>com.javacode2018</groupId>
    <artifactId>rt</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>

import:这个比较特殊,后面的文章中单独讲,springboot和springcloud中用到的比较多。


*******:scope在运行范围中有效,意思是指依赖的jar包会被打包到运行包中,最后运行时被添加到classpath中运行


6、依赖的传递
假如项目中引入了a,而a又依赖b和c,这时候maven就会自动的把b和c给导入进来,这就是依赖的传递。依赖的传递会受到scope的影响
假如a依赖于b,b依赖于c,我们说a对于b是第一直接依赖,b对于c是第二直接依赖,a对于c是传递性依赖,而第一直接依赖的scope和第二直接依赖的scope决定了传递依赖的范围,即决定了a对于c的scope的值。
下面我们用表格来列一下这种依赖的效果,表格最左边一列表示第一直接依赖(即A->B的scope的值),而表格中的第一行表示第二直接依赖(即B->C的scope的值),行列交叉的值显示的是A对于C最后产生的依赖效果。


理解:以a对b的scope是complie,b对c的scope是runtime为例
a是在什么时候都需要b,但是当在运行项目时,b这时也是要运行了,就需要引入c。a对c的传递依赖就是在runtime了

7、maven依赖调节功能


现实中可能存在这样的情况,a->b->c->d(1.0),a->h->d(2.0),此时d出现了两个版本,maven会选择d的那个版本呢?
解决这种问题,有两个原则:
路径最近原则:
上面d的2.0版本离a更近一点,所以会选择2.0。
如果路径一样呢?像a->b->d(1.0),a->h->d(2.0)
这时候遵循 最先声明原则:
就看b和h在pom文件中谁最先声明

8、可选依赖
当 a->b:compile
b->c:compile 时,
c会传递到a,假如b不想c让a依赖,怎办么,就使用optional元素,在b依赖c的过程中将optional的值设置为true

9、排除依赖
a->b,b->c(1.0),这两个scope都是compile,此时c会传递到a,但是a要使用c的2.0版本,不能让c传递过来,就可以在b->c中的exclusions
元素中配置,如:
<exclusions>
<exclusion>
<groupId>c的groupid<groupId/>
       <artifactId>c的artifactId<artifactId/>
<exclusion/>
<exclusions/>









原文地址:https://www.cnblogs.com/fbbg/p/13064546.html