软件培训网站,阿里巴巴有几个网站是做外贸的,建站之星做网站,五个网站页面4. Maven的依赖管理
在 Java 开发中#xff0c;项目的依赖管理是一项重要任务。通过合理管理项目的依赖关系#xff0c;我们可以有效的管理第三方库#xff0c;模块的引用及版本控制。而 Maven 作为一个强大的构建工具和依赖管理工具#xff0c;为我们提供了便捷的方式来管…4. Maven的依赖管理
在 Java 开发中项目的依赖管理是一项重要任务。通过合理管理项目的依赖关系我们可以有效的管理第三方库模块的引用及版本控制。而 Maven 作为一个强大的构建工具和依赖管理工具为我们提供了便捷的方式来管理项目的依赖。
4.1 什么是依赖范围
Maven 的依赖构件包含一个依赖范围的属性。这个属性描述的是三套 classpath 的控制即编译、测试、运行也就是添加的 jar 包起作用的范围。Maven 提供了以下几种依赖范围compile、test、provided、runtime、system。在 pom.xml 文件中使用 标签指定依赖范围类型。
1、compile
编译依赖范围。
依赖范围对于编译、测试、运行 3 种 classpath 都有效。如果没有指定默认使用该依赖范围。
也就是说compile 在整个项目下和运行时都有效Maven 都会帮助导包 2、test
测试依赖范围。
此依赖范围只对编译测试、运行测试的 classpath 有效在编译主代码、运行项目时无法使用此类依赖。比如 junit它只有在编译测试代码、运行测试时才需要。
当依赖范围是 test 时
在 src/test 目录下Maven 会帮忙导入这些包。出了此范围Maven 不会导包。
示例
在 maven_01_javase 模块中我们在 pom.xml 中引入 junit 依赖并指定依赖范围为 test
这时候我们可以在 src/test 包下的测试类 MathCalculateTest 中导入 junit 包
然后把 MathCalculateTest 类复制粘贴到 src/main 包下发现无法将 junit 包导入到类中。这是因为junit 的依赖范围是 test它只能在 src/test 包下引入。 3、provided
已提供依赖范围。
此依赖范围对于编译源码、编译测试、运行测试中 classpath 有效但在运行时无效因为项目的运行环境中已经提供了所需要的构件。比如上面说到的 servlet-api这个在编译和测试的时候需要用到但是在运行的时候Web 容器已经提供了就不需要 Maven 帮忙引入了。Tomcat 服务器就提供了 servlet-api
示例
1、在 maven_03_javawebnew 模块中我们在 pom.xml 文件中引入了 servlet、junit、和 MyBatis并分别指定依赖范围为 provided、test 和 compile。
2、然后我们将 maven_03_javawebnew 模块打包以观察 war 包中的 jar 包引入情况。
3、打包后得到了模块的 war 包
4、将 war 包后缀修改为 rar 压缩包格式并打开发现只有一个 MyBatis 的 jar 包
因此在打包时compile 依赖范围的 jar 包 Maven 会帮忙引入而 test 和 provided 依赖范围的 jar 包 Maven 不会引入。
4、runtime
运行时依赖范围。
此依赖范围对于测试和运行项目的 classpath 有效但在编译时无效比如 JDBC 驱动实现项目代码编译的时候只需要使用 JDK 提供的 JDBC 接口运行的时候才需要具体的 JDBC 驱动实现。
dependenciesdependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.32/versionscoperuntime/scope/dependency
/dependencies5、system
系统依赖范围。
此依赖直接依赖于本地路径。可能每个开发者机器中构件的路径不一致如果使用这种写法你的机器中可能没有问题别人的机器中就会有问题所以建议谨慎使用。
需要使用 标签显式地指定依赖文件的路径。
总测试
1、在 maven_03_javawebnew 模块中引入 5 种类型的依赖
dependencies!--添加 Servlet 的依赖--dependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion4.0.1/versionscopeprovided/scope/dependency!--添加 junit 的依赖--dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.2/versionscopetest/scope/dependency!--添加 MyBatis 的依赖--dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.11/versionscopecompile/scope/dependency!--添加 MySQL 的依赖--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.32/versionscoperuntime/scope/dependency!--添加 maven_01_javase 的依赖--dependencygroupIdcn.myphoenix/groupIdartifactIdmaven_01_javase/artifactIdversion1.0-SNAPSHOT/versionscopesystem/scopesystemPathC:/Coding_Gallery/Intellij_IDEA_Workspace/learning_maven/maven_01_javase/target/maven_01_javase-1.0-SNAPSHOT.jar/systemPath/dependency/dependencies2、将 maven_03_javawebnew 打包观察 jar 包引入情况 发现只有 complie 和 runtime 类型的依赖可以导入其他 3 种类型未导入。
作用范围总结
依赖范围的再理解
在项目中引入依赖的同时我们可以为依赖指定依赖范围。从这个角度来看依赖范围就是当前项目希望引入的依赖jar 包在什么范围内起作用在什么范围内不起作用。
4.2 什么是依赖传递
依赖具有传递性。
Maven 的依赖传递机制不管 Maven 项目存在多少间接依赖POM 中都只需要定义其直接依赖不必定义任何间接依赖间接依赖都由 Maven 自动导入这在一定程度上简化了 POM 的配置。
假项目 A 依赖项目 B项目 B 依赖项目 C则 A 直接依赖 BB 直接依赖 C而 A 间接依赖 C。
示例说明间接依赖
现在maven_01_javase 模块中引入了 junit 1 个依赖。为了演示直接依赖和间接依赖现在在该模块中引入 spring-context 依赖。 引入依赖后发现 spring-context 这个一级依赖下又引入了多个间接依赖这些间接依赖都是由 Maven 自动引入的。
4.2.1 依赖范围对依赖传递的影响
B 是 A 的直接依赖C 是 A 的间接依赖根据 Maven 的依赖传递机制间接依赖 C 会以传递性依赖的形式引入到 A 中但这种引入并不是无条件的它会受到依赖范围的影响。
举例说明本例子依据为下图
1、创建 2 个项目 maven_laoda 和 maven_mazai在 maven_laoda 引入 maven_mazai则 maven_mazai 是 maven_laoda 的直接依赖在 maven_mazai 中引入 MyBatis 依赖则 MyBatis 是 maven_laoda 的间接依赖。
2、在 maven_mazai 中执行 install将 maven_mazai 安装到本地仓库在公司中是安装到私服然后在 maven_laoda 中通过 GAV 引入 maven_mazai 依赖。
3、在 maven_laoda 的 pom.xml 中为 maven_mazai 设置不同的依赖范围。同时在 maven_mazai 的 pom.xml 中为 MyBatis 设置不同的依赖范围。观察
在 maven_laoda 中能否引入间接依赖 MyBatis如果能引入那么间接依赖 MyBatis 的依赖范围类型是什么
接下来进行测试。固定间接依赖的类型变动直接依赖的类型。
测试 1间接依赖为 compile直接依赖为 compile。
测试结果间接依赖被引入到 maven_laoda 中依赖范围是 compile。
测试 2间接依赖为 compile直接依赖为 test。
测试结果间接依赖被引入到 maven_laoda 中依赖范围是 test。
……
测试 n间接依赖为 test直接依赖为 compile。
测试结果间接依赖没有被引入到 maven_laoda 中。
……
测试 n m间接依赖为 provided直接依赖为 compile。
测试结果间接依赖没有被引入到 maven_laoda 中。
……
测试 n k间接依赖为 runtime直接依赖为 compile。
测试结果间接依赖被引入到 maven_laoda 中依赖范围是 runtime。
总结
当间接依赖的范围是 compile 时间接依赖在项目中的范围与直接依赖的范围一致当间接依赖的范围是 test 或 provided时间接依赖不会被传递当间接依赖的范围是 runtime 时间接依赖在项目中的范围与直接依赖的范围一致但 compile 例外此时间接依赖在项目中的范围为 runtime。
4.3 依赖冲突
4.3.1 什么是依赖冲突
在 Maven 项目中依赖通常被定义在项目的 pom.xml 文件中。当引入同一个依赖的多个不同版本时就会发生依赖冲突。
这可能是因为项目的直接依赖和间接依赖导致了同一库的多个版本存在于类路径中。每个显式声明的类包都会依赖于一些其他的隐式类包这些隐式的类包会被 Maven 间接引入进来从而造成类包冲突。
4.3.2 依赖冲突的解决方案
Maven 可以通过以下途径解决依赖冲突。
4.3.2.1 版本锁定
在父工程中使用 标签进行版本锁定。
dependencyManagement 可以统一管理整个项目的版本号确保项目的各个模块的依赖版本一致。dependencyManagement 只是声明依赖并不会在项目中引入依赖。子项目需要显式地声明需要用的依赖可以不指定版本其与项目声明的版本一致。如果排斥父工程中定义的版本号可以显式地进行版本号声明。
示例
1、创建 maven_parent 模块在该模块下创建子模块 maven_son观察两个模块的 pom.xml 文件
父模块指定了 GAV子模块只指定了 A其 G 和 V 继承自父模块。在父模块中声明了子模块在子模块中声明了父模块。父模块中指明了打包方式是 pom。pom 是父工程的打包方式此工程不能运行只是来进行规范和定义的。
2、在 maven_parent 模块的 pom.xml 文件中使用 dependencyManagement 声明 junit 依赖的版本红框。然后在子模块 maven_son 的 pom.xml 文件中引入 junit 依赖不需要指明版本使用父模块中声明的版本橙色框。
另外可以看到父模块并没有引入 junit 依赖绿框。
3、可选可以在子模块中指定 junit 的版本而不使用父模块中声明的版本。但这样做的意义不大因为使用 dependencyManagement 就是要做版本锁定的。
4、继承可以不使用 dependencyManagement而是直接在父模块中使用 dependency 引入依赖。这样的话子模块不需要显式地引入依赖因为它会继承父模块的所有依赖。
dependencyManagement 的版本锁定可以在子模块中按需引入依赖而继承****会使子模块拥有父模块的所有依赖使子模块依赖变得臃肿。
4.3.2.2 短路径优先
路径短者优先引入。顾名思义当一个间接依赖存在多条引入路径时引入路径短的会被解析使用。如下图所示maven_02 会引入 junit 的 4.12 版本而不是 4.13 版本。
示例
1、创建模块 maven_02 和 maven_03。
2、在 maven_02 中引入 junit 4.12 版本。
3、执行 install将 maven_03 安装到本地仓库在 maven_02 中引入 maven_03。
3、在 maven_03 中引入 junit 4.13 版本。
结果如下在 maven_02 中引入直接依赖 junit 4.12而丢弃间接依赖 junit 4.13 4.3.2.3 声明优先
如果存在短路径则优先选择短路径。如果路径相同则先声明者优先POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用顺序靠前的优先使用。如下图所示 示例
1、创建模块 maven_01、maven_04 和 maven_05。
2、在 maven_04 中引入 junit 4.13 版本在 maven_05 中引入 junit 4.12 版本。
3、执行 install将 maven_04 和 maven_05 安装到本地仓库在 maven_01 中引入 maven_04 和 maven_05。
结果如下
如果 maven_04 的 GAV 在前则 maven_01 中引入间接依赖 junit 4.13而丢弃间接依赖 junit 4.12如果 maven_05 的 GAV 在前则 maven_01 中引入间接依赖 junit 4.12而丢弃间接依赖 junit 4.13。
4.3.2.4 特殊优先后来者居上
在同一个 pom.xml 文件中对同一个 jar 包进行了多次不同版本的配置后面的版本覆盖前面的版本。这种情况比较少见。
示例
1、创建模块 maven_06。
2、在 maven_06 的 pom.xml 文件中依次引入 junit 4.13、4.8.2 和 4.12 版本。
结果如下在 maven_06 中引入直接依赖 junit 4.12而覆盖了 junit 4.8.2 和 junit 4.13 4.3.2.5 可选依赖
使用 标签禁止传递间接依赖。
如果当前项目被依赖到其它项目中当前项目可以拒绝交出间接依赖项。如下图所示maven_07 添加了 maven_08 的依赖maven_08 可以自主设置其依赖项 junit 4.13 是否被间接传递。true 为不传递间接依赖那么在 maven_07 项目中就没有 junit 4.13 的依赖。默认是 false传递间接依赖。 示例
1、创建模块 maven_07 和 maven_08。
2、在 maven_08 的 pom.xml 文件中引入 junit 4.13 版本。
3、执行 install将 maven_08 安装到本地仓库在 maven_07 中引入 maven_08。
结果如下在 maven_07 中引入了直接依赖 maven_08但没有引入间接依赖 junit 4.13 4.3.2.6 排除依赖
当前项目主动排除其依赖项目的间接依赖。也就是控制当前项目是否使用其直接依赖传递下来的接间依赖。如下图所示在 maven_09 项目中添加 maven_10 项目的依赖但不要 maven_10 项目中的 junit 4.13 依赖可以使用排除依赖这样可以保证当前项目依赖的纯净性。 排除依赖使用 exclusions 元素排除依赖
exclusions 元素下可以包含若干个 exclusion 子元素用于排除若干个间接依赖该元素包含两个子元素groupId 和 artifactId用来确定需要排除的间接依赖的坐标信息。exclusion 元素中只需要设置 groupId 和 artifactId 就可以确定需要排除的依赖无需指定版本 version也无法指定版本 version
示例
1、创建模块 maven_09 和 maven_10。
2、在 maven_10 的 pom.xml 文件中引入 junit 4.13 版本。
3、执行 install将 maven_10 安装到本地仓库在 maven_09 中引入 maven_10。
结果如下在 maven_09 中引入了直接依赖 maven_10但没有引入间接依赖 junit 4.13 附可选依赖和排除依赖的区别
排除依赖和可选依赖都能在项目中将间接依赖排除在外但两者实现机制却完全不一样。
可选依赖是自己决定是否向外提供间接依赖。排除依赖是主动拒绝添加直接依赖关联的间接依赖。可选依赖的优先级高于排除依赖若对于同一个间接依赖同时使用排除依赖和可选依赖那么可选依赖的取值必须为 false否则排除依赖无法生效。
4.4 Intellij IDEA刷新依赖的8种方式
在 IDEA 中有时候会出现刷新延时的情况那么需要进行手工刷新依赖。
点击 M 刷新按钮。点 Maven 窗口的 Reload All Maven Projects。Build — ReBuild Project 重新构建项目的同时刷新所有依赖。点击本项目的 pom.xml 文件 — 右键 — Maven — Reload Project 刷新本项目的依赖。打开 pom.xml 文件全选、拷贝、删除、关闭、打开、粘贴物理刷新 pom.xml 文件 。Invalidate Caches — 全选 — Invalidate and Restart 清空 IDEA 的缓存并重启 IDEA 刷新依赖。打开本地仓库搜索 last全选删除点 Maven 的刷新全部依赖的按钮。在 7 的步骤后执行 File — settings — Build,Execution,Deployment — Build Tools — Maven — Repositories — 选中本地仓库 — update — OK。
4.5 资源文件的指定
问题
src/main/java 和 src/test/java 这两个目录中的所有 *.java 文件会分别在 comile 和 test-comiple 阶段被编译编译结果分别放到了 target/classes 和 targe/test-classes 目录中。但是这两个目录中的其他文件后缀是 .properties 或 .xml 等文件都会被忽略掉编译后丢失。简单来说就是在 resources 目录下的 *.properties 文件和 *.xml 文件编译时不丢失但 resources 目录外的 *.properties 文件和 *.xml 文件会丢失。
解决方案
如果需要把 src 目录下的除 .java 之外的文件包放到 target/classes 目录作为输出的 jar 一部分。需要指定资源文件位置将内容放到 标签中。
演示一不指定配置文件的路径在编译时会丢失
1、新建 maven_11 模块。
2、在 src/main/java/cn/myphoenix 路径下创建 2 个配置文件hello.xml 和 larry.properties。
3、在 src/main/resources 路径下创建 2 个配置文件jdbc.properties 和 SqlMapConfig.xml。
4、编译 maven_11 模块查看配置文件情况。 结果如下图所示
src/main/java/cn/myphoenix 路径下的 2 个配置文件在编译后消失了。src/main/resources 路径下的 2 个配置文件依旧存在。
演示二将配置文件的路径配置在 pom.xml 文件中解决编译丢失问题
1、将两个路径下的配置文件都配置到 pom.xml 文件中。
buildresources!--src/main/java路径--resourcedirectorysrc/main/java/directoryincludesinclude**/*xml/includeinclude**/*properties/include/includes/resource!--src/main/resources路径--resourcedirectorysrc/main/resources/directoryincludesinclude**/*xml/includeinclude**/*properties/include/includes/resource/resources
/build2、clean 并重新编译 maven_11 模块查看配置文件情况成功。 附pom 配置项解读 buildresourcesresourcedirectorysrc/main/resources/directoryincludesinclude**/*xml/includeinclude**/*properties/include/includes/resource/resources
/build解释 这是 Maven 构建配置的根元素包含了所有与构建相关的配置信息。用于定义项目中的资源文件。资源文件是指那些不需要编译但需要包含在最终构建输出中的文件例如配置文件、静态文件等。表示一个资源文件集的配置。src/main/resources指定资源文件的来源目录这里是项目的 src/main/resources 目录。定义哪些文件应该被包含在构建过程中。 标签内可以包含多个 子标签每个子标签指定一个文件匹配模式。 **/*xml匹配所有以 .xml 结尾的文件无论它们位于 src/main/resources 目录下的哪个子目录中。**/表示递归匹配所有子目录因此即使文件位于深层子目录中也会被包含进来。**/*properties匹配所有以 .properties 结尾的文件无论它们位于 src/main/resources 目录下的哪个子目录中。 附在 big-marketing-13119-lwh-app 的 pom.xml 中同样配置了配置文件的路径 resourcesresourcedirectorysrc/main/resources/directoryfilteringtrue/filteringincludesinclude**/**/include/includes/resource
/resourcestestResourcestestResourcedirectorysrc/test/resources/directoryfilteringtrue/filteringincludesinclude**/**/include/includes/testResource
/testResources