首先一个概念是:在Maven中我们把jar,war文件称为构件。
Maven定义了这样一组规则:世界上任何一个我们所需要的构建都可以使用Maven坐标来获得,而Maven坐标包括groupId,artifactId,version,packaging,classifier.
坐标详解:
- groupId:定义当前Maven项目隶属的实际项目,这里面有一个概念,Maven项目和实际项目并不是一一对应的关系,因为可能对一个大的实际项目A,我们会按照功能对其进行拆分,拆分开来的一个一个子项目,所以我们将子项目称之为Maven项目,把父项目A称之为实际项目。所以我们应该把gruopId写成隶属的组织或者公司再加上实际项目的名称。
- artifactId:该元素定义了一个实际项目中的一个Maven项目,推荐使用实际项目的名称作为其前缀。默认的情况Maven生成的构建,其文件名会以artifactId作为开头,例如我们设置artifactId为nexus-indexer,则生成的构件为:nexus-indexer-2.0.0.jar
- version:该元素定义了Maven项目所处的版本。
- packaging:该元素定义了Maven项目的打包方式。包括war和jar,默认为jar。
- classifier:用来帮助定义构件输出的一些附属构件,因为某些项目可能会用插件生成一些例如帮助文档,源码包这样的附属构件,注意:我们不能定义classifier,因为附属的构件不是项目默认生成的,而是由附加的插件帮助生成。
依赖的配置:
根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个项目依赖,每个依赖可以包含的元素有:
- groupId,artifactId,version:依赖的基本坐标,根据这个坐标找到我们项目需要依赖的相应构件。
- type:依赖的类型,对应于上述坐标所指向的构件的packaging,一般不需要我们声明
- scope:依赖范围,在说这个概念之前,需要知道,Maven在编译项目主代码的时候需要使用使用一套编译classpath,例如编译项目主代码的时候需要用到spring-core,该文件以依赖的方式引入到classpath中,而Maven在编译和执行测试的时候需要使用一套编译classpath,而最后实际运行Maven项目的时候又会使用另一套运行classpath,所以我们的依赖范围就是控制依赖与这三种classpath(编译classpath,运行classpath,测试classpath)的关系,即依赖需要引入到哪个classpath中。
-
- compile:如果我们没有指定scope,默认为这个,使用此依赖范围,对编译classpath,运行classpath,测试classpath我们的依赖的构件均有效,典型的例如:spring-core构件
- test:测试范围依赖,只对测试classpath有效,典型的例如:JUit构件,只在编译和执行测试的时候使用
- provided:对于编译classpath,测试classpath有效。典型的例如:servlet-api,编译和测试的时候均须使用,但是在实际运行项目的时候,这个它会由容器提供,所以我们不在引入这个依赖。
- runtime:只对测试classpath和运行classpath有效,典型的例如:JDBC驱动实现,项目主代码的编译只需要jdk提供的JDBC接口,而只有在测试和运行时,才需要上述具体的JDBC驱动。
- system:对于编译classpath,测试classpath有效。但是使用这个依赖范围,必须通过systemPath元素显式地指定依赖文件的路径,即在<scope>system</scope>标签下在加上一个标签<systemPath>...</systemPath>
- optional:标记依赖是否可选
- exclusions:用来排除依赖性传递
传递性依赖:
这里我们假设一个应用场景,我们的项目A是基于spirng框架,所以我们需要配置spring-framework的依赖到项目的pom.xml文件中,但是spring-framework可能还会依赖其他的构件,那这个问题该怎么解决,其实构件本身已经帮我们解决了,我们以打开仓库的spring-core-4.2.5.RELEASE为例:
可以发现除了有构件spring-core-4.2.5.RELEASE.jar外,还有一个spring-core-4.2.5.RELEASE.pom的pom文件,打开发现:
在pom文件中,我们发现了spring-core-4.2.5.RELEASE.jar构件在dependencies中定义的对其他构件的依赖,我们使用Maven的传递性依赖机制,假设项目A对spring-core有一个compile范围的依赖,spring-core对commos-logging又有一个compile范围的依赖,那么commas-logging就会成为项目A的compile范围依赖,他就是项目A的一个传递性依赖。
有了传递性依赖机制,在使用spirng框架的时候我们就不用再考虑他依赖了什么,也不用担心引入了多余的依赖,因为Maven会解析各个直接依赖的POM,将那些必须的间接依赖,以传递性依赖的形式引入到当前项目中。
但是这里又会涉及到一个问题,如果传递依赖的时候它们之间的依赖范围不一致怎么办,例如:项目A对sping-core的依赖是compile范围的,但是spring-core对commas-logging的依赖是test范围的,那传递过来的项目A对commas-logging的依赖是什么范围的呢? 这里我们用一张表来展示,其中最左边一列是第一直接依赖范围,最上面一行是第二直接依赖范围,中间交叉的是传递性依赖范围,“-”表示依赖不会得到传递:
按照我们上面的例子,项目A对sping-core的依赖是第一直接依赖范围,它是compile范围的,spring-core对commas-logging的依赖是第二直接依赖范围,它是test范围,则此时依赖不会得到传递。