maven的几种maven依赖配置文件范围

为了账号安全,请及时绑定邮箱和手机
我看了maven的依赖范围,但是我没懂这个依赖范围在实际项目中有什么作用?
我看了maven的依赖范围,但是我没懂这个依赖范围在实际项目中有什么作用?
你说的这个scope不应该是“测试”“编译”“运行”么?这个scope应该配置的是这个依赖能使用的范围吧?视频里是这么说的啊,如果配置的是test,那么只会在测试的过程中才会使用这个依赖,而在编译和运行的时候是不会使用这个依赖的
写下你的评论...
写下你的评论...
写下你的评论...
Copyright (C) 2018 imooc.com All Rights Reserved | 京ICP备 号-11发布于 03/10 13:02
最近在工作中,使用Dubbo调用远程服务,需要依赖被调用方(dubbo service provider)提供的一些jar包。
下面是maven和dubbo的相关配置。
&!-- 远程dubbo服务 --&
&dependency&
&groupId&com.dubbo.service.provider&/groupId&
&artifactId&foo-api&/artifactId&
&version&1.0&/version&
&/dependency&
dubbo-serivce-provider.xml
&dubbo:reference id=&fooService& interface=&com.dubbo.service.provider.fooService& check=&false& url=&${dubbo.foo.server.address}&/&
项目启动后,出现如下异常
java.lang.NoSuchMethodError: com.google.common.base.Platform.systemNanoTime()
通过Eclipse查看依赖树发现,foo-api所依赖的jar与项目中的jar发生了冲突。
可以将如上场景抽象为下面的逻辑:
因为Maven拥有传递依赖的特性,因此真实的依赖树是:
因此D项目发生了依赖冲突。
相关知识:依赖传递(Transitive Dependencies)
依赖传递(Transitive Dependencies)是Maven 2.0开始的提供的特性,依赖传递的好处是不言而喻的,可以让我们不需要去寻找和发现所必须依赖的库,而是将会自动将需要依赖的库帮我们加进来。
例如A依赖了B,B依赖了C和D,那么你就可以在A中,像主动依赖了C和D一样使用它们。并且传递的依赖是没有数量和层级的限制的,非常方便。
但依赖传递也不可避免的会带来一些问题,例如:
当依赖层级很深的时候,可能造成循环依赖(cyclic dependency)
当依赖的数量很多的时候,依赖树会非常大
针对这些问题,Maven提供了很多管理依赖的特性:
依赖调节(Dependency mediation)
依赖调节是为了解决版本不一致的问题(multiple versions),并采取就近原则(nearest definition)。
举例来说,A项目通过依赖传递依赖了两个版本的D:
A -& B -& C -& ( D 2.0) ,
A -& E -& (D 1.0),
那么最终A依赖的D的version将会是1.0,因为1.0对应的层级更少,也就是更近。
依赖管理(Dependency management)
通过声明Dependency management,可以大大简化子POM的依赖声明。
举例来说项目A,B,C,D都有共同的Parent,并有类似的依赖声明如下:
A、B、C、D/pom.xml
&dependencies&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-a&/artifactId&
&version&1.0&/version&
&exclusions&
&exclusion&
&groupId&group-c&/groupId&
&artifactId&excluded-artifact&/artifactId&
&/exclusion&
&/exclusions&
&/dependency&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-b&/artifactId&
&version&1.0&/version&
&type&bar&/type&
&scope&runtime&/scope&
&/dependency&
&/dependencies&
如果父pom声明了如下的Dependency management:
Parent/pom.xml
&dependencyManagement&
&dependencies&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-a&/artifactId&
&version&1.0&/version&
&exclusions&
&exclusion&
&groupId&group-c&/groupId&
&artifactId&excluded-artifact&/artifactId&
&/exclusion&
&/exclusions&
&/dependency&
&dependency&
&groupId&group-c&/groupId&
&artifactId&artifact-b&/artifactId&
&version&1.0&/version&
&type&war&/type&
&scope&runtime&/scope&
&/dependency&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-b&/artifactId&
&version&1.0&/version&
&type&bar&/type&
&scope&runtime&/scope&
&/dependency&
&/dependencies&
&/dependencyManagement&
那么子项目的依赖声明会非常简单:
A、B、C、D/pom.xml
&dependencies&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-a&/artifactId&
&/dependency&
&dependency&
&groupId&group-a&/groupId&
&artifactId&artifact-b&/artifactId&
&!-- 依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar.--&
&type&bar&/type&
&/dependency&
&/dependencies&
依赖范围(Dependency scope)
Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另一套classpath,实际运行项目的时候,又会使用一套classpath。
依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系的,Maven有以下几种依赖范围:
compile: 编译依赖范围。
如果没有指定,就会默认使用该依赖范围。
使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
test: 测试依赖范围。
使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。
典型例子是JUnit,它只有在编译测试代码及运行测试的时候才需要。
provided: 已提供依赖范围。
使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。
典型例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
runtime: 运行时依赖范围。
使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。
典型例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
system: 系统依赖范围。
该依赖与三种classpath的关系,和provided依赖范围完全一致。但使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。
systemPath元素可以引用环境变量:
&dependency&
&groupId&com.system&/groupId&
&artifactId&foo&/artifactId&
&version&1.0&/version&
&scope&system&/scope&
&systemPath&${maven.home}/lib/foo.jar&/systemPath&
&/dependency&
import(Maven 2.0.9及以上): 导入依赖范围。
我们知道,maven的继承和java是一样的,只能单继承。因此,父pom可能非常庞大,如果你想把依赖分类清晰的进行管理,就更不可能了。
import scope依赖能解决这个问题。你可以把Dependency Management放到单独用来管理依赖的pom中,然后在需要使用依赖的模块中通过import scope依赖,就可以引入dependencyManagement。
例如,父pom.xml:
&modelVersion&4.0.0&/modelVersion&
&groupId&com.test.sample&/groupId&
&artifactId&base-parent1&/artifactId&
&packaging&pom&/packaging&
&version&1.0.0-SNAPSHOT&/version&
&dependencyManagement&
&dependencies&
&dependency&
&groupId&junit&/groupId&
&artifactid&junit&/artifactId&
&version&4.8.2&/version&
&/dependency&
&dependency&
&groupId&log4j&/groupId&
&artifactid&log4j&/artifactId&
&version&1.2.16&/version&
&/dependency&
&/dependencies&
&/dependencyManagement&
&/project&
通过非继承的方式来引入这段依赖管理配置:
&dependencyManagement&
&dependencies&
&dependency&
&groupId&com.test.sample&/groupId&
&artifactid&base-parent1&/artifactId&
&version&1.0.0-SNAPSHOT&/version&
&type&pom&/type&
&scope&import&/scope&
&/dependency&
&/dependencies&
&/dependencyManagement&
&dependency&
&groupId&junit&/groupId&
&artifactid&junit&/artifactId&
&/dependency&
&dependency&
&groupId&log4j&/groupId&
&artifactid&log4j&/artifactId&
&/dependency&
注意:import scope只能用在dependencyManagement里面
排除依赖(Excluded dependencies)
排除不需要从所依赖的项目中传递过来的依赖,好比你买车的时候,主动跟卖车的说明不需要买车附加的保险业务。下面在解决思路中会举例说明。
可选依赖(Optional dependencies)
被依赖的项目主动不把可以传递的依赖传递下去,好比卖车的主动声明自己不会让买车的人买这辆车附加的保险业务。下面在解决思路中会举例说明。
有了上面的知识背景,考虑使用Maven提供的Optional和Exclusions来控制依赖的传递。
Optional 定义后,该依赖只能在本项目中传递,不会传递到引用该项目的父项目中,父项目需要主动引用该依赖才行。
&dependency&
&groupId&com.bar&/groupId&
&artifactId&B&/artifactId&
&version&1.0&/version&
&optional&true&/optional&
&/dependency&
这种情况下,A对B的依赖将不会传递给D.
Exclusions 则是主动排除子项目传递过来的依赖。
&dependency&
&groupId&com.bar&/groupId&
&artifactId&A&/artifactId&
&version&1.0&/version&
&exclusions&
&exclusion&
&groupId&com.bar&/groupId&
&artifactId&B&/artifactId&
&/exclusion&
&/exclusions&
&/dependency&
这种情况下,D对A的依赖将不会包含B.
开始提到的问题就是通过exclusion的方式解决的。
Maven的依赖机制(Dependency Mechanism)是Maven最著名的特性,并且是Maven在依赖管理领域中最令人称道的。因此,对Maven的依赖机制有深入的理解,对使用Maven非常必要。
& 著作权归作者所有
人打赏支持
码字总数 8321
:bowtie: 本文的github pages blog:https://ijiangtao.github.io//java/maven/MavenDependencyMechanism/
评论删除后,数据将无法恢复
其中一个Maven的核心特征是依赖管理。管理依赖关系变得困难的任务一旦我们处理多模块项目(包含数百个模块/子项目)。 Maven提供了一个高程度的控制来管理这样的场景。 传递依赖发现 这是很通...
依赖机制介绍 依赖管理是Maven众所周知的特性之一,也是Maven擅长的领域之一.管理单个项目的依赖并不是太困难,但是当你开始处理由数十个甚至上百个模块组成的多模块项目或者应用时,Maven将会很...
moonsnake777
Maven作为一个十分流行的项目管理工具,在我们的项目变得逐渐复杂的时候,可以有效的降低项目管理难度。 聚合工程就是当项目到达一定规模时,十分有效的管理子项目和模块的方法。 首先介绍一...
Dependency management plugin 提供类似 Maven 依赖管理和排除功能. 根据配置的依赖管理元数据,此插件将会控制你项目的版本、传递依赖,并且将执行你项目中的所有排除声明(exclusions decl...
依赖管理有大量的术语,为了方便理解整个依赖管理的过程,我们先要理解一下相关的术语 配置 配置是一组实现特殊目的的依赖项集合:比如配置表示编译项目的一组依赖集合。配置可以访问底层的已...
lastsweetop
场景是这样的: 有项目 A 和 项目 B,都是maven项目。 A用maven打包成jar叫A.jar,B依赖于A.jar。 但是A.jar 所依赖的那些 jar,却必须在 B的 dependecy 中再次声明一遍 无法传递依赖。 但是...
1 . 坐标   maven 的所有构件均通过坐标进行组织和管理。maven 的坐标通过 5 个元素进行定义,其中 groupId、artifactId、version 是必须的,packaging 是可选的(默认为jar),classifie...
一、What`s Maven?   Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说就是用来管理项目所...
hafiz.zhang
文章作者:Tyan 博客:noahsnail.com 更多Spring框架内容请到作者博客查看,持续更新。 2.Spring框架介绍 Spring框架是一个为支持开发Java应用提供全面基础架构的Java平台。Spring处理基础架...
Quincuntial
标签 : Java基础 Maven 是每一位Java工程师每天都会接触的工具, 但据我所知其实很多人对Maven理解的并不深, 只把它当做一个依赖管理工具(下载依赖、打包), Maven很多核心的功能反而没用上....
没有更多内容
加载失败,请刷新页面
谈谈神秘的ES6——(三)ES6的函数 ES6函数的改变不算太大,都是一些其他语言早就有的功能,而Javascript一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函...
Confluence 支持一些可以从 Java 系统属性中配置的配置参数和调试(debugging )设置。系统属性通常是使用 -D 为参数选项,这个选项是 Confluence 在运行后设置到 JVM 虚拟机中的。请参考:C...
https://blog.csdn.net/qiaoji6073/article/details/
看不懂就多看几遍
Shell编程 (Ⅰ) 一、shell脚本介绍 shell是一种脚本语言 和传统的开发语言比较,会比较简单 shell有自己的语法;可以使用逻辑判断、循环等语法 可以自定义函数,目的就是为了减少重复的代码...
无数据项计数的循环队列算法: 在判满和判空需要注意,由于是循环队列,所以有可能rear和front指针指向同一位置,但是出现的情况有很多,造成可空可满的情况,所以数据项要比存储空间少一,这...
沉迷于编程的小菜菜
第十二章、学习Shell Scripts 12.3 善用判断式 12.4 条件判断式 12.4.2 利用case ... esac 判断 hello-3.sh #!/bin/bash# Program:# Show "hello" from $1... by using case ... esac# ......
Nginx Header,实现对HTTP/S请求、响应进行添加、修改、删除等操作 通过Nginx内置 文档地址: http://nginx.org/en/docs/http/ngx_http_headers_module.html http://nginx.org/en/docs/http/...
前言 作为本系列的起始章节,本章节主要是对Docker的相关概念进行简单阐述下。自此也是查阅了相关资料,奈何也都是英文版居多,看的是有点头大的。现在悔不当初不好好学习英文了。o(︶︿︶)...
本文永久跟新地址:https://my.oschina.net/bysu/blog/.创建本地空白版本库mygit git init 2.往本地仓库添加文件testFile.txt(该文件需放在上面仓库所在的目录) vi testFile.txt ...
不最醉不龟归
Algorithm 739. Daily Temperatures - LeetCode 535. Encode and Decode TinyURL - LeetCode 811. Subdomain Visit Count - LeetCode 706. Design HashMap - LeetCode 771. Jewels and Stone......
没有更多内容
加载失败,请刷新页面
文章删除后无法恢复,确定取消删除此文章吗?
亲,自荐的博客将通过私信方式通知管理员,优秀的博客文章审核通过后将在博客推荐列表中显示
确定推荐此文章吗?
确定推荐此博主吗?
聚合全网技术文章,根据你的阅读喜好进行个性推荐
指定官方社区
深圳市奥思网络科技有限公司版权所有本文隶属于新手图文教程《IDEA+Maven搭建Spring+SpringMVC+Mybatis项目骨架》
目录 & 项目地址:
本文即将介绍各个子模块的依赖配置。在此之前,有必要先明确一下依赖传递、依赖范围和类加载器等概念,确保新手能清晰地理解配置的原理。下面摘录study-common的pom.xml作为例子:
study-parent/study-common/pom.xml
&?xml version="1.0" encoding="UTF-8"?&
&project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&
&artifactId&study-parent&/artifactId&
&groupId&com.oschina&/groupId&
&version&1.0-SNAPSHOT&/version&
&modelVersion&4.0.0&/modelVersion&
&!-- ### groupId,version直接继承自Parent,packaging默认为jar ### --&
&!-- &groupId&com.oschina&/groupId& --&
&artifactId&study-common&/artifactId&
&!-- &version&1.0-SNAPSHOT&/version& --&
&!-- &packaging&jar&/packaging& --&
&dependencies&
&dependency&
&groupId&junit&/groupId&
&artifactId&junit&/artifactId&
&!-- 版本-version,已由Parent进行约定,省略 --&
&!-- &version&${junit.version}&/version& --&
&!-- 包类型-type,默认为jar,省略 --&
&!-- &type&jar&/type& --&
&!-- 依赖范围-scope,默认为compile,此处已由Parent约定为test,省略 --&
&!-- &scope&compile&/scope& --&
&!-- 是否可选-optional,默认为false,省略 --&
&!-- &optional&false&/optional& --&
&!-- 排除(传递的)依赖,此处不需要,省略 --&
&!-- &exclusions&&/exclusions& --&
&/dependency&
&!-- json处理器 --&
&dependency&
&groupId&com.alibaba&/groupId&
&artifactId&fastjson&/artifactId&
&/dependency&
&!-- 包括了若干google的java项目广泛依赖的核心库 --&
&dependency&
&groupId&com.google.guava&/groupId&
&artifactId&guava&/artifactId&
&/dependency&
&!-- slf4j的核心包(它只是一套接口) --&
&dependency&
&groupId&org.slf4j&/groupId&
&artifactId&slf4j-api&/artifactId&
&/dependency&
&!-- slf4j对log4j12的支持包,把slf4j的接口转嫁到log4j上去实现 --&
&dependency&
&groupId&org.slf4j&/groupId&
&artifactId&slf4j-log4j12&/artifactId&
&/dependency&
&/dependencies&
&/project&
一、依赖的传递
1.&传递依赖
在maven里,依赖是会传染的。传染有两种方式,一种是Sub Module对Parent Module的继承依赖,另一种就是依赖传递。首先,如红1在maven插件中选中study-common,然后点击红2的图标,生成study-common的依赖关系图如下:
红色区域中,study-common后面有五条连接线,分别对应五个在pom中配置的依赖。而junit和slf4j-log4j12后面各还有一条连接线,连接着两个陌生的依赖。它们是从哪冒出来的?现在从junit入手,追溯一下hamcrest-core的来源。我们先到maven本地仓库找一下junit/pom.xml:
如图中红色区域所示,Junit项目依赖于hamcrest-core,导致study-common间接地依赖hamcrest-core。这就是传递依赖,其规则是:A依赖B,B依赖C,那么A依赖C。
在上一张依赖关系图中,与study-common直接相连的五个就是“直接依赖”,间接通过junit与study-common相连的hamcrest-core就是“传递依赖”。
2. 可选依赖
举个栗子,我在店铺了买了一个USB充电器。除了这个充电器,店家还卖一些配件,例如USB小台灯、USB小风扇、USB充电数据线等等。如果我想点个小灯,我可以买那个小台灯接上;我觉得热,可以要了那个小风扇;我手机没电,就接上充电数据线。
在上面这个例子中,USB充电器就是我的一个依赖。但是这个组件可能还需要其他一些配件才能工作。这些可选的配件被配置为optional=true(可选依赖),我想用哪一个配件,就显式地在我的pom上配置该配件的依赖。
这就是含有可替换特性的包了,例如日志功能,你给配log4j他能用,给配logback也能用。这种情况在后面解释scope的时候会提到。
特殊一点的情况是,我买的这个东西其实是一个多功能插座,插座上带有USB插口。我买配件让USB插口发挥作用也行,不买配件,它仍然是个略贵但能正常工作的插座。
这就好比含可选特性的包,你要用到它的某项特性,就手动配置该特性所需要的依赖,而不会自动传递。
3. 依赖冲突
既然有传递依赖,就不可避免的会出现下图这种情况:
如图,因为传递依赖机制,项目study-A既依赖于项目study-D:1.0版本,又依赖于项目study-D:2.0版本,造成依赖冲突。解决这个问题有两个办法,一个是通过手工排除其中一个依赖;另一个就是由maven自行通过算法去选择。
算法有两种:
选择传递依赖 - 同样是传递依赖,maven优先选传递路径短的那个。 如上图中的D:2.0,它离A只差一个节点;而D:1.0离A差两个节点。
选择直接依赖 - maven优先选配置在前面的那个(老版本没有这个特性)。 例如同一个pom里面约定了两个study-F,先约定的是2.0,后约定的是1.0,maven选2.0那个。
4. 依赖排除
好了,如上所说,出现了依赖冲突或者我想将某个依赖替换为同类的其他实现怎么办?用依赖排除。
以下是:study-parent/pom.xml/dependencyManagement的一段
&!-- Spring核心工具包 --&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-core&/artifactId&
&version&${spring.version}&/version&
&exclusions&
&!-- Spring官方建议不要用这个 --&
&exclusion&
&groupId&commons-logging&/groupId&
&artifactId&commons-logging&/artifactId&
&/exclusion&
&/exclusions&
&/dependency&
二、依赖的范围
解释依赖范围之前,需要先简单说明一下类加载器。
1. 类加载器 - ClassLoader
Java创造了一套自己的指令系统(字节码),并号称这套指令可以运行在任何系统上,也就是传说中的跨平台。实际上呢,人家操作系统根本就不认识字节码。Java之所以能跑起来,是因为他们给不同的平台分别定制了一个能运行在该平台上的Java虚拟机(Java Virtual Machine,简称 JVM),然后依靠JVM来把字节码解释为操作系统能看懂的机器码,从而实现跨平台运行。
而JVM呢,基本是用C、C++和汇编写的,这也就是为什么在程序员鄙视链中Java会排在C++后面了...
假设现在要跑一个Java程序 - Tomcat7:
java org.apache.catalina.startup.Bootstrap
首先会启动JVM。
JVM有一个内置的程序叫Bootstrap ClassLoader。它会加载$JAVA_HOME/jre/lib/rt.jar。 这不是前端的那个bootstrap框架,而是一个用C++写的java类加载器。 rt即runtime,是个实现Java运行时环境的基础包,包含了java.lang.String等所有基础类。
刚加载的基础类,有个sun.misc.Launcher类,定义了另外两个用Java写的类加载器Extension ClassLoader和APP ClassLoader,其中,Extension ClassLoader会先加载$JAVA_HOME/jre/lib/ext/下的扩展包。然后,APP ClassLoader会到$ClassPath目录加载其他系统包。
正常情况下,当APP ClassLoader收到了一个请求,要获取org.apache.catalina.startup.Bootstrap,它将做以下尝试(上源码,更直观):
protected Class&?& loadClass(String name, boolean resolve)
throws ClassNotFoundException
synchronized (getClassLoadingLock(name)) {
// 首先,检查该class是否已经加载
Class&?& c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
// 如果没有加载,检查是否有“父加载器”
// 如果有,交给父加载器的loadClass
// 如果没有,交给Bootstrap
// 即责任链模式
if (parent != null) {
c = parent.loadClass(name, false);
c = findBootstrapClassOrNull(name);
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
if (c == null) {
// 如果父加载器没有返回该类,就在本加载器环节尝试加载
long t1 = System.nanoTime();
c = findClass(name);
// this is the d record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
if (resolve) {
resolveClass(c);
源码在$JAVA_HOME目录下的src.zip里面。
用时序图来表示如下图(没找到bootstrap源码,其内部逻辑是我猜的)
上述继承自ClassLoader类的loadClass方法利用了责任链模式,它实现以下内容:
每个类首次被加载时,是按类加载器父子结构的顶端到底端依次加载的。加载后,类会被缓存在相应的加载器一段时间。当类加载器收到请求时,如果该类还在某一个类加载器缓存内,就是逐步向上委托查找缓存时找到的;如果已经不在缓存里了,就是从Bootstrap开始往下尝试加载时加载的,这被称为委派机制。
通过委派机制,子类加载器可以看见父类加载器加载过的类,反之不成立,这被称为可视机制。
同一个JVM里面,判断两个类是否相同的依据是类的全路径名称和类加载器名称是否完全相同。但由于委派机制的存在,每个类只要在父类加载器哪里被找到,子类加载器就不会再尝试加载了,因此,同名的类只能被加载一次,这被称为单一原则。
以上机制能保证Java依赖被按次序层层加载,并避免核心类被恶意篡改。
到了这里,就可以执行org.apache.catalina.startup.Bootstrap.main()了。然后问题来了,上面三个默认的类加载器只加载固定目录下的类,那由谁来加载我们的webapp呢?答案是tomcat自己实现的类加载器。
如上图所示:
JVM的APP类加载器加载了tomcat的几个核心包以支持其启动 - tomcat临时覆盖了系统的ClassPath,改为tomcat的启动支持包
Tomcat的Common类加载器加载了tomcat的运行支持包及对web的支持包。
Tomcat的webapp加载器加载web程序的包以支持web程序运行。
发现没有,上图跟委派机制的流程完全不同!因为tomcat的webapp类加载器重写了loadClass方法,刻意违反了向上委托原则。
2. 依赖范围&- scope
大致理解了类加载器的工作机制,就可以开始看第三方依赖的依赖范围了。首先要明确,对于一个第三方依赖,可以把使用场景分成以下五种:
测试编译时
测试运行时
对应这些使用场景,maven设计了scope(依赖范围),把依赖的范围分为compile、provided、runtime、test、system和import六种。注意,只要配置了第三方依赖,无论是哪一种scope,IDEA都会把它列入External Libaraies进行索引,以用作在我们开发时弹出相应的代码补全提示等用途。
下面我们来看前四种Scope:
★ compile
当一个第三方依赖在上述五种场景都要用到时,即选用compile这种范围。这也是scope的默认值。在打包war时,该类依赖会被一起打包。
★ provided
IDEA、Javac、maven-compile-plugin的本质都是Java程序,其运行需依赖于JVM实例,自然也就加载了上文所说的那些基础类包和扩展类包。在自己的程序中引入、使用这些类包时是不需要配置依赖的。但是,对于只在运行时才由容器提供的那些类包,例如tomcat的servlet API,是这几个IDE/工具所不了解的。
因此,如果你要使用如servlet API进行开发,就必须配置相应的依赖,并把scope配置为provided。意思是,我开发时请帮我索引以实现代码补全提示,编译时请加载这些包以校验我的代码正确性,但是,不要打包,因为容器会提供这些包。
更具体地说,如果我依赖了servlet API并且没有配置scope,那么我的war包里边就会含有servlet-api.jar。按照tomcat的类加载顺序,启动时已经由Common ClassLoader加载了一个servlet-api,运行时如需加载servlet-api,请求会发往Webapp ClassLoaderwar,而该加载器会先到我的webapp里找到并发现我包里的servlet-api,结果就是一个jvm里面出现了两个相同的包以及包里相同或版本有差异的类,导致报错(官方文档提到webapp ClassLoader会刻意忽略掉webapp里面的servlet-api包,但实测发现仍会报错)。
★ runtime
为了解耦,我们经常是面向接口编程而不是面向具体实现类编程,最终打包的时候,就分成了接口和实现两种包。在开发和编译时,我们调用一个方法,IDEA和Javac并不要求知道具体的实现类,只要我们引用的依赖能告诉它们接口就行。这样一来,只要把scope配置为runtime,就可以不参与编译,但是会被打包,因为测试时和运行时都要加载该依赖。
举个栗子来说明这种scope的应用场景:
开发一个ORM框架,连接数据库部分全部面向JDBC接口编程,然后通过Spring来注入具体的实现类。这时候,项目的pom.xml就可以同时配置Mysql、Oracle、Sqlserver的实现类依赖,scope设置为runtime,并配置为optional(可选依赖)。然后运行的时候,只需要修改Spring的配置文件,改变注入的实现,就可以分别连接不同类型的数据库。
其他项目引用该依赖时,因为orm/pom.xml里JDBC实现类都配置成了optional,故项目不会传递这些实现类的依赖,此时要根据具体环境手动配置一个实现类。例如我们给Mybatis配置的Mysql连接包。
test依赖就简单了,就是Junit一类的测试框架准备的,用在test类开发、test类编译(测试编译)和test类运行(测试运行)三种场景。
依赖范围会影响依赖的传递,例如范围为test的依赖并不会被传递。详见maven官方文档。
三、依赖配置
基于以上两点说明,各模块的依赖关系就比较清晰了。study-plugin是预留的模块,只依赖于junit和study-common,暂不做其他配置。
1. study-blog
如第三天所说的工程结构变体,这个模块的作用其实是Aggregator(聚合器)而不是Parent(父模块),所以这个模块不需要配置依赖,而在多子系统的结构下需要配置的build部分此时已经放在了study-parent中,故也不需要配置。
2. study-blog-pojo
pojo基于贫血模型,只有领域模型的属性和get/set方法,其他行为全部放在了逻辑层。因此它不需要任何依赖。
3. study-blog-mapper
根据mapper的职责,他需要能通过orm框架连接数据库,并将数据与po实体绑定。那么它需要依赖pojo、mybatis、mysql驱动和连接池,后续开发时还要加入一些增强Mybatis的插件。
&dependencies&
&dependency&
&groupId&com.oschina&/groupId&
&artifactId&study-blog-pojo&/artifactId&
&version&${project.version}&/version&
&/dependency&
&!-- ##### 数据库相关 ##### --&
&!-- Mybatis框架 --&
&dependency&
&groupId&org.mybatis&/groupId&
&artifactId&mybatis&/artifactId&
&/dependency&
&!-- Mybatis与spring的适配器.Spring没有对mybatis的支持包,mybatis就自己写了个 --&
&dependency&
&groupId&org.mybatis&/groupId&
&artifactId&mybatis-spring&/artifactId&
&/dependency&
&!-- MySql驱动 --&
&dependency&
&groupId&mysql&/groupId&
&artifactId&mysql-connector-java&/artifactId&
&/dependency&
&!-- 连接池 --&
&dependency&
&groupId&com.alibaba&/groupId&
&artifactId&druid&/artifactId&
&/dependency&
&/dependencies&
4. study-blog-service
作为三层架构最厚的一层,service几乎用到了Spring的大部分特性。虽然诸如Ioc相关的依赖放在离tomcat启动最近的web模块也没有问题,但出于职责划分以及service模块需要实现独立于controller进行测试的考虑,在这一层引入依赖。
&dependencies&
&dependency&
&groupId&com.oschina&/groupId&
&artifactId&study-blog-mapper&/artifactId&
&version&${project.version}&/version&
&/dependency&
&dependency&
&groupId&com.oschina&/groupId&
&artifactId&study-plugin&/artifactId&
&version&${project.version}&/version&
&/dependency&
&!-- Spring的运行时环境 --&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-context&/artifactId&
&/dependency&
&!-- Spring对AspectJ框架的支持包 --&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-aspects&/artifactId&
&/dependency&
&!-- Spring对jdbc的简化封装 --&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-jdbc&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&!-- Spring对事务控制的实现模块 --&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-tx&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&/dependencies&
实际依赖的Spring模块有:
实际依赖的模块数量远多于配置的数量,而且都是必须的依赖。对于这个问题,有两种观点:
通过传递依赖,大幅减少了配置的代码量;而且采用官方推荐的传递依赖,稳定性更佳。
在非正式版本中,Spring-context及其传递的依赖可能分别属于两个临近的小版本,很容易出现依赖冲突,建议明确手动配置所有依赖(即在上面配置中加入Spring-core、Spring-beans、Spring-aop等)。
这里采用的是4.3.6的最终发布版本,我检查过了,所有模块及其依赖版本一致,故采用观点1的做法。
5. study-controller
&dependencies&
&dependency&
&groupId&com.oschina&/groupId&
&artifactId&study-blog-service&/artifactId&
&version&${project.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-webmvc&/artifactId&
&/dependency&
&/dependencies&
同样,通过spring-webmvc和study-service的传递,controller模块依赖了大部分Spring模块。图片较大,请自行通过插件查看依赖关系图。
6. study-web
这里后续可能会加入与前端交互相关的依赖,例如文件上传组件等。
&dependencies&
&dependency&
&groupId&com.oschina&/groupId&
&artifactId&study-blog-controller&/artifactId&
&version&${project.version}&/version&
&/dependency&
&!-- JSP相关 --&
&dependency&
&groupId&jstl&/groupId&
&artifactId&jstl&/artifactId&
&/dependency&
&!-- 其实tomcat7支持最新的3.0版本,支持异步servlet.不过稳定起见,仍用2.5版本 --&
&dependency&
&groupId&javax.servlet&/groupId&
&artifactId&servlet-api&/artifactId&
&/dependency&
&!-- 同上,最新2.2,这里采用最常用的2.0版本 --&
&dependency&
&groupId&javax.servlet&/groupId&
&artifactId&jsp-api&/artifactId&
&/dependency&
&/dependencies&
完整代码以Github项目为准。
& 著作权归作者所有
人打赏支持
码字总数 31823
坐标 Maven的坐标用来唯一标示一个项目在Maven仓库的位置,Maven的坐标主要由groupId、artifactId、version、packaging、classifier 5个元素组成,其中groupId、artifactId、version是必要的...
一、What`s Maven?   Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说就是用来管理项目所...
hafiz.zhang
image.png 前言 夜空中最亮的星,2018请照亮我前行~ Maven是我们日常开发都会用到的,新年第一天,把看过的Maven基础概念做了整理,作为入门和查阅使用。 正文 Maven概念 Maven作为一个构建工...
英文:Project Object Model;中文:项目对象模型。 项目版本 主版本.次版本.增量版本.限定版本号: &major version&.&minor version&.&incremental version&-&qualifier&一个版本“5”只有主......
SimpleClean
依赖机制介绍 依赖管理是Maven众所周知的特性之一,也是Maven擅长的领域之一.管理单个项目的依赖并不是太困难,但是当你开始处理由数十个甚至上百个模块组成的多模块项目或者应用时,Maven将会很...
moonsnake777
文章作者:Tyan 博客:noahsnail.com 更多Spring框架内容请到作者博客查看,持续更新。 2.Spring框架介绍 Spring框架是一个为支持开发Java应用提供全面基础架构的Java平台。Spring处理基础架...
Quincuntial
  【IT168 资讯】Apache家族一直备受程序员的关注,继Apache Maven 3.3.9之后,直接跳到3.5.0,至于3.4.0,程序员怕是看不到了,不过也没什么关系,功能够强大就足以!来看看是哪些新功能足...
看着简单而又复杂的pom.xml文件,看似熟悉,当自己编写的时候觉得简单,但是看人家项目的时候又觉得复杂的很,现在我们一起来分析这个pom文件。 Maven的坐标为各种构件引入了秩序,任何一个构...
1.maven的环境搭建 网上搭建maven环境的文档有很多,这里就不赘述了 具体可以参看http://www.cnblogs.com/quanyongan/archive//3025971.html 下面开始上手maven 2. 构建一个 hell...
KongFanhao
1 . 坐标   maven 的所有构件均通过坐标进行组织和管理。maven 的坐标通过 5 个元素进行定义,其中 groupId、artifactId、version 是必须的,packaging 是可选的(默认为jar),classifie...
没有更多内容
加载失败,请刷新页面
谈谈神秘的ES6——(三)ES6的函数 ES6函数的改变不算太大,都是一些其他语言早就有的功能,而Javascript一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函...
Confluence 支持一些可以从 Java 系统属性中配置的配置参数和调试(debugging )设置。系统属性通常是使用 -D 为参数选项,这个选项是 Confluence 在运行后设置到 JVM 虚拟机中的。请参考:C...
https://blog.csdn.net/qiaoji6073/article/details/
看不懂就多看几遍
Shell编程 (Ⅰ) 一、shell脚本介绍 shell是一种脚本语言 和传统的开发语言比较,会比较简单 shell有自己的语法;可以使用逻辑判断、循环等语法 可以自定义函数,目的就是为了减少重复的代码...
无数据项计数的循环队列算法: 在判满和判空需要注意,由于是循环队列,所以有可能rear和front指针指向同一位置,但是出现的情况有很多,造成可空可满的情况,所以数据项要比存储空间少一,这...
沉迷于编程的小菜菜
第十二章、学习Shell Scripts 12.3 善用判断式 12.4 条件判断式 12.4.2 利用case ... esac 判断 hello-3.sh #!/bin/bash# Program:# Show "hello" from $1... by using case ... esac# ......
Nginx Header,实现对HTTP/S请求、响应进行添加、修改、删除等操作 通过Nginx内置 文档地址: http://nginx.org/en/docs/http/ngx_http_headers_module.html http://nginx.org/en/docs/http/...
前言 作为本系列的起始章节,本章节主要是对Docker的相关概念进行简单阐述下。自此也是查阅了相关资料,奈何也都是英文版居多,看的是有点头大的。现在悔不当初不好好学习英文了。o(︶︿︶)...
本文永久跟新地址:https://my.oschina.net/bysu/blog/.创建本地空白版本库mygit git init 2.往本地仓库添加文件testFile.txt(该文件需放在上面仓库所在的目录) vi testFile.txt ...
不最醉不龟归
Algorithm 739. Daily Temperatures - LeetCode 535. Encode and Decode TinyURL - LeetCode 811. Subdomain Visit Count - LeetCode 706. Design HashMap - LeetCode 771. Jewels and Stone......
没有更多内容
加载失败,请刷新页面
文章删除后无法恢复,确定取消删除此文章吗?
亲,自荐的博客将通过私信方式通知管理员,优秀的博客文章审核通过后将在博客推荐列表中显示
确定推荐此文章吗?
确定推荐此博主吗?
聚合全网技术文章,根据你的阅读喜好进行个性推荐
指定官方社区
深圳市奥思网络科技有限公司版权所有

我要回帖

更多关于 maven依赖配置文件 的文章

 

随机推荐