依赖是一种关系。一个项目使用的A这个jar包,我们就说这个项目依赖A。我们正是通过依赖的方式,告诉maven我们需要哪些jar包。
依赖的配置
回顾一下,我们之前建立的测试maven项目,默认里面已经有junit的依赖了,我们看一下项目的pom.xml文件,里面有以下配置:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies>
我们通过在dependencies元素下,使用dependencie元素,配置相应的坐标,告诉maven我们需要的依赖,一个dependencie元素就代表一个依赖。我们来看一下依赖的配置选项:
GAV:依赖的基本坐标,基本坐标是最重要的;
type:依赖的类型,对应packaging,默认值为jar;
scope:依赖的范围,关于依赖的范围,下文会详细阐述;
optional:标记依赖是否可选;
exclusion:用于排除传递性依赖。
在传统的项目中,我们是通过将jar文件添加到lib目录,添加到classpath中来添加依赖,而在maven项目中,我们只需通过配置依赖,maven就知道我们需要添加那些jar包。
依赖的范围
在说明依赖的范围前,我们来回顾下java中的classpath的概念。classpath用于指定类搜索路径。在安装jdk是,我们需要配置classpath环境变量,这样我们在运行程序时就能找到jdk中的类。同样的,我们依赖的第三方jar,也需要添加到相应的classpath,这样我们自己编写的程序才能在编译或者是运行时找到需要的class。
maven定义了三套classpath,编译classpath,测试classpath,运行classpath,依赖范围用于控制依赖与三种classpath的关系。
compile:编译依赖范围,对编译、测试、运行三种classpath都有效,说白了,就是你在主代码、测试代码、运行时都能使用这个范围的jar包中的class,配置依赖是,依赖范围默认是compile;
test:测试依赖范围,只对测试classpath有效,说白了,就是只能在test代码中才能引用该范围的jar中的class,如junit的jar,我们只在测试的时候需要用到,主代码或者运行期间,不需要用到junit的jar;
provided:已提供依赖范围,对编译和测试classpath有效,对运行时classpath无效,如servlet-api,j2ee应用服务器,如tomcat等,已经提供了,我们在运行期间无需再添加;
runtime:运行时依赖范围,对测试和运行时classpath有效,对编译时classpath无效,如JDBC驱动,我们在写代码时是针对jdbc的接口编程,只有在实际运行,连接数据库时,在会具体使用到某个数据库的驱动。
传递性依赖
回顾一下我们以往开发项目的经历,比如,我们在使用spring框架的时候,仅仅加入spring的jar包还不够,因为spring还使用到了commons-loggingjar包,我们需要把这个jar包也添加进来才能运行,也就是项目A依赖与B,而B又依赖于C,我么称A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。maven能自动处理传递性依赖。
第一直接依赖和第二直接依赖的范围决定了传递性依赖的范围
蓝色部分,左侧竖列是第一直接依赖,顶部横排是第二直接依赖,交叉部分是传递性依赖的范围,也就是传递性依赖对哪种classpath生效。举个例子,我们的项目依赖了spring的jar,而spring的jar依赖commons-logging的jar,如果两者都是compile范围的依赖,那么我们的项目对于commons-logging也是compile范围的依赖。
依赖的调解及排除
有的时候,我们会遇到这种情况,A->B->C->X(1.0),A->D->X(2.0),项目A依赖B,B又依赖C,而C依赖X的1.0版本,A同时依赖D,而D依赖X的2.0版本,那么,A对于D是传递性依赖,maven会将哪个版本的X添加进来呢?maven解析依赖遵循以下两个原则:
第一原则:路径最近者优先
第二原则:第一申明者优先
A->B->C->X(1.0),路径长度为3,而A->D->X(2.0)路径长度为2,根据第一原则,maven会将X的2.0版本添加进来,而忽略1.0版本;如果两个传递性依赖路径一样长,那么,在pom文件中,先配置的依赖(顺序靠前)优先。
有的时候我们想明确排除某个传递性依赖,可以通过在dependency元素下配置exclusions、exclusion元素,并指定排除的依赖的做包,将其排除
如上图所示,默认commons-beanutils依赖commons-logging,通过exclusion将其排除,这样maven就不会讲commons-logging添加到classpath中。
我们来回顾一下,到目前为止,我们安装了maven,通过maven的archetype创建了项目的骨架,接下来我们在项目的pom文件中,通过配置依赖(dependency)告诉maven我们需要哪些jar包,maven解析dependency中的GAV定位相应的jar包,然后根据scope将其加入到相应的classpath。一切看起来很完美,我们只是在pom文件中配置了dependency元素,其他的啥都没做,那么maven是从什么地方去找这些jar包的呢?传统的构建方式,需要我们自己手动去官网或者其他地方找jar包,然后添加,那么maven是去什么地方找这些jar包的呢?这就是仓库的概念,我们在下一章阐述。