maven 依赖 一时失言乱红尘 2022-07-15 07:47 208阅读 0赞 **依赖 ** dependency除了包含,groupId,artifactId,version等基本元素外,还会包含一些其他的元素。 **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependency****>** 2. **<****groupId****>****</****groupId****>** 3. **<****artifactId****>****</****artifactId****>** 4. **<****version****>****</****version****>** 5. **<****type****>****</****type****>** 6. **<****scope****>****</****scope****>** 7. **<****optional****>****</****optional****>** 8. **<****exclusions****>** 9. **<****exclusion****>****</****exclusion****>** 10. **</****exclusions****>** 11. **</****dependency****>** groupId、artifactId、version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven可以根据坐标才能找到需要的依赖。 type:依赖的类型,对应项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar。 scope:依赖的范围 optional:标记依赖是否可选。 exclusions:用来排除传递依赖。 # 依赖的范围 # 范围用scope表示。首先需要知道Maven在编译主代码时需要使用一套classpath。在account-email实例中,编译主代码需要用到spring-core, 该文件以依赖的方式被引入到classpath中。其次,Maven在编译与执行测试的时候使用另一套classpath。上例中的JUnit就是一个很好的例子。 该文件也以依赖的方式引入到测试使用的classpath中,不同的是这里的依赖范围是test。最后实际运行Maven项目的时候,又会使用一套classpath, 上例中的spring-core需要在classpath中,而JUnit不需要。 依赖范围就是用来控制依赖于这三种classpath(编译classpath、测试classpath、运行classpath)的关系:Maven有如下几种依赖类型: **complile:**编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。 典型的例子就是spring-core,在编译,测试和运行都需要。 **test:**测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时无法使用此类依赖。典型的例子 是Junit,它只有在编译测试代码及运行测试代码时需要。 **provided:**已提供依赖范围。使用此范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目 的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。 **runtime:**运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行的classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现, 项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。 **system:**系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖是必须通过systemPath元素显 式地指定依赖文件路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建不可移植,因此应该谨慎使用。systemPath 元素可以引用环境变量,如: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependency****>** 2. **<****groupId****>**javax.sql**</****groupId****>** 3. **<****artifactId****>**jdbc-stdext**</****artifactId****>** 4. **<****version****>**2.0**</****version****>** 5. **<****scope****>**system**</****scope****>** 6. **<****systemPath****>**$\{java.home\}/lib/rt.jar**</****systemPath****>** 7. **</****dependency****>** **import:**(Maven2.0.9及以上):导入依赖范围。该依赖不会对三种classpath产生实际的影响,将在介绍8.3.3 Maven依赖时进行详细介绍。 # 传递性依赖 # ## 何为传递依赖 ## 考虑一个基于Spring Framework的项目,如果不使用Maven,那么在项目中就需要手动下载相关依赖。由于Spring Framework又会依赖于其他开源类库,因此 实际中往往会下载个很大的如spring-framework-2.5.6-with-dependencies.zip的包,这里包含了所有的spring-Framework的jar包,以及所有它依赖的其他jar包。 这么做往往就引入了很多不必要的依赖。另一种做法就是指下载spring-framework-2.5.6.zip这样一个包,这里不包含其他相关依赖,到实际使用的时候,在根据 出错信息,或者查询相关文档,加入需要的其他依赖,显然,这也是一件非常麻烦的事情。 Maven的传递性依赖机制很好地解决了这个问题。已account-email项目为例,该项目有一个org.springframwork:spring-core.2.5.6的依赖,而实际上spring-core 也有自己的依赖,我们可以直接访问位于中央仓库的该构建的POM:http://repo1.maven.org/maven2/org/springframework/spring-core/2.5.6/spring-core-2.5.6.pom, 该文件包含了一个commons-logging依赖, **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependency****>** 2. **<****groupId****>**commons-logging**</****groupId****>** 3. **<****artifactId****>**commons-logging**</****artifactId****>** 4. **<****version****>**1.1.1**</****version****>** 5. **</****dependency****>** 该依赖没有声明依赖范围,那么其依赖范围就是默认的compile。同时回顾一下account-email,spring-core的依赖范围也是compile。 account-mail有一个compile范围的spring-core依赖,spring-core有一个compile范围的依赖commons-logging依赖,那么commons-logging 就会成为account-email的complile范围依赖,commons-logging是account-email的一个传递性依赖: account-emai---->spring-core----->commons-logging -----------------------------> 有了这个传递性依赖机制,在使用Spring Framework的时候就不用去考虑它依赖了什么,也不用担心引入多余的依赖。Maven会解析各个直接依赖 POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。 ## 传递性依赖和依赖范围 ## 依赖范围不仅可以控制依赖与三种classpath 的关系,还对传递性依赖产生影响。 **第一直接依赖/第二直接依赖 complile test provided runtime** **complile(测试、编译、运行) ** complile X X runtime **test(测试) ** test X X test **provided(测试,编译) ** provided X X provided **runtime(测试、运行) ** runtime X X runtime # 依赖调节 # 例如项目A存在依赖关系,A->B->C->X(1.0)、A->D->X(2.0) 第一原则路径最短的优先,因此X(2.0)会被解析使用。 A->B->Y(1.0),A-C->Y(2.0),第二原则,第一声明者优先,如果B的声明依赖在C前,则Y(1.0)会被解析使用。 # 可选依赖 # 项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选)。根据传递性依赖的定义,如果 所有第三个依赖范围都是compile,那么X,Y就是A的compile范围传递性依赖。然而,由于这里X,Y是可选依赖,依赖将不会得以依赖。换句话说,X,Y 将不会对A有任何影响。 为什么要使用这一特性呢?可能项目B实现了两个特性,其中的特性一依赖于X,特性二依赖于Y,而且这两个特性是互斥的,用户不可能同时使用两个特 性。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使 用这个工具包的时候,只会依赖一种数据库。 **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependencies****>** 2. **<****dependency****>** 3. **<****groupId****>**junit**</****groupId****>** 4. **<****artifactId****>**junit**</****artifactId****>** 5. **<****version****>**3.8.1**</****version****>** 6. **<****scope****>**test**</****scope****>** 7. **</****dependency****>** 8. **<****dependency****>** 9. **<****groupId****>**mysql**</****groupId****>** 10. **<****artifactId****>**mysql-connector-java**</****artifactId****>** 11. **<****version****>**5.1.10**</****version****>** 12. **<****optional****>**true**</****optional****>** 13. **</****dependency****>** 14. **<****dependency****>** 15. **<****groupId****>**postgresql**</****groupId****>** 16. **<****artifactId****>**postgresql**</****artifactId****>** 17. **<****version****>**8.4-701.jdbc3**</****version****>** 18. **<****optional****>**true**</****optional****>** 19. **</****dependency****>** 20. **</****dependencies****>** 使用<optional>标识依赖是可选依赖,他们只对当前项目B产生影响,当其他项目依赖于B的时候,这两个依赖不会被传递。因此,当项目A依赖于项目B的时候,如果其实际使用基于[MySQL][]数据库,那么需要在项目A中就需要显式地声明mysql-connector-java这一个依赖。 # 最佳实践 # 本小节归纳Maven依赖常见的一些技巧,方便用来避免和处理很多常见的的问题。 ## 排除依赖 ## 传递性依赖会给项目隐式引入很多依赖,这极大的简化了项目依赖的管理,但是有些时候这些特性也会带来问题。例如当前项目有一个第三方 依赖,而第这个第三方依赖由于某种原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而 SNAPSHOT版本的稳定性会直接影响到当前项目,这时就需要排除该SNAPSHOT,并且在该项目中声明该类库的某个正式发布的版本。 还有一种情况,你可能也想要替换某个传递性依赖,比如Sun JTA API,Hibernate 依赖于这个JAR,但是由于版权的因素,该类库不在中央 仓库中,而Apache Geronimo项目中有一个对应的实现,这是就可以排除Sun JTA API,在声明Geronimo的JTA API的实现。 **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****project****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****groupId****>**com.liuxm.com**</****groupId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****artifactId****>**project-a**</****artifactId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****version****>****</****version****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependencies****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependency****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****groupId****>**com.liuxm.group**</****groupId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****artifactId****>**project-b**</****artifactId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****version****>**1.0.0**</****version****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****exclusions****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****exclusion****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****groupId****>**com.liuxm.group**</****groupId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****artifactId****>**project-c**<****artifactId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****exclusion****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****exclusions****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****dependency****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****dependency****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****groupId****>**com.liuxm.group**</****groupId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****artifactId****>**project-c**</****artifactId****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****version****>**1.1.0**</****version****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****dependency****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****dependencies****>** **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **</****project****>** 上述代码中,项目A依赖项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显示声明对项目C1.1.0的依赖,代码中 使用exclusions元素声明排除依赖,exclusions可包含一个或多个exclusion,因此可以排除一个或者多个传递性依赖。需要注意的是, 声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中某个 依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。 A----->B-----X----C(version?) -----C(version1.10) ## 归类依赖 ## 在前面已经提过,有很多关于spring framework的依赖,它们分别是org.springframework:spring-cre:2.5.6/org .springwork:spring-beans:2.5.6、org.springframework:spring-context:2.5.6和org.springframework:spring-context-support:2.5.6, 它们来自同一项目的不同模块。因此,所有这些依赖的版本都是相同的,而且可以预见,如果将来需要升级spring framework, 这些依赖版本会一起升级。 **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****project** xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"**>** 3. **<****modelVersion****>**4.0.0**</****modelVersion****>** 4. 5. **<****groupId****>**com.lxm.mkfriend**</****groupId****>** 6. **<****artifactId****>**guileidependency**</****artifactId****>** 7. **<****version****>**0.0.1-SNAPSHOT**</****version****>** 8. **<****packaging****>**jar**</****packaging****>** 9. 10. **<****name****>**guileidependency**</****name****>** 11. **<****url****>**http://maven.apache.org**</****url****>** 12. 13. **<****properties****>** 14. **<****project.build.sourceEncoding****>**UTF-8**</****project.build.sourceEncoding****>** 15. **<****springframework.version****>**2.5.6**</****springframework.version****>** 16. **</****properties****>** 17. 18. **<****dependencies****>** 19. **<****dependency****>** 20. **<****groupId****>**org.springframework**</****groupId****>** 21. **<****artifactId****>**spring-core**</****artifactId****>** 22. **<****version****>**$\{springframework.version\}**</****version****>** 23. **</****dependency****>** 24. **<****dependency****>** 25. **<****groupId****>**org.springframework**</****groupId****>** 26. **<****artifactId****>**spring-beans**</****artifactId****>** 27. **<****version****>**$\{springframework.version\}**</****version****>** 28. **</****dependency****>** 29. **<****dependency****>** 30. **<****groupId****>**org.springframework**</****groupId****>** 31. **<****artifactId****>**spring-context**</****artifactId****>** 32. **<****version****>**$\{springframework.version\}**</****version****>** 33. **</****dependency****>** 34. **<****dependency****>** 35. **<****groupId****>**org.springframework**</****groupId****>** 36. **<****artifactId****>**spring-context-support**</****artifactId****>** 37. **<****version****>**$\{springframework.version\}**</****version****>** 38. **</****dependency****>** 39. **</****dependencies****>** 40. **</****project****>** 这里简单用了Maven属性,首先使用properties元素定义Maven定义属性,该例中定义了一个springframework.version子元素,其值为2.5.6. 有了这个属性定义之后,Maven运行的时候会将POM中的所有的$\{springframwork.version\}替换成实际值2.5.6. ## 优化依赖 ## 通过本章内容,已经了解到,Maven会自动解析所有项目的直接依赖和传递性依赖,并且根据规则正确判断每个依赖的范围, 对于一些依赖冲突,也能进行调节,也确保任何一个构件只有唯一的版本在依赖中存在。在这些工作之后,最后得到的那些依赖 被称为已解析依赖。可以运行当前项目的已解析依赖: mvn dependency:list mvn dependency:tree mvn dependency:analyze [view plain]: http://blog.csdn.net/zndxlxm/article/details/51812341# [CODE]: https://code.csdn.net/assets/CODE_ico.png [CODE_CODE]: https://code.csdn.net/snippets/1751059 [ico_fork.svg]: https://code.csdn.net/assets/ico_fork.svg [ico_fork.svg 1]: https://code.csdn.net/snippets/1751059/fork [MySQL]: http://lib.csdn.net/base/14
还没有评论,来说两句吧...