继上篇httpclient引入依赖冲突报错之后出现的问题,又出现错误
java.lang.NoSuchMethodError: org.apache.http.util.Args.containsNoBlanks(Ljava/lang/CharSequence;Ljava/lang/String;)Ljava/lang/CharSequence; at org.apache.http.HttpHost.<init>(HttpHost.java:80) at org.apache.http.client.utils.URIUtils.extractHost(URIUtils.java:370) at org.apache.http.impl.client.CloseableHttpClient.determineTarget(CloseableHttpClient.java:92) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
依赖冲突是解决了,但是偶然性的出错
其实问题消失了不代表事情就可以结束了,原因是什么,为什么会出现这个问题? 做技术要有打破砂锅问到底的精神。
看项目中有很多文件有用到ThreadSafe这个注解,主要是有两个jar包,一个是httpclient
, 一个是它自己所在的包httpcore
。查看httpclient的pom.xml
,写的也比较明确,httpclient会依赖于httpcore包.
<dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <scope>compile</scope> </dependency> .... </dependencies>
groupId
, artifactId
比较容易理解,用于确定jar包的坐标,<scope>
表示范围,<version>
在哪儿呢?
查看httpclient在META-INF/DEPENDENCIES
中写明了Apache HttpCore org.apache.httpcomponents:httpcore:jar:4.4.4
。需要依赖于4.4.4的版本。我怀疑DEPENDENCIES文件只是起到一个说明作用,并不会做什么动作。应该是这样的,本身我的项目是spring-boot的,spring boot支持对jar包的版本进行统一管理,这个以后文章说明。META-INF/DEPENDENCIES文件如下
二、关于scope
我们在httpclient中看到了dependency的依赖范围是compile
,这个scope
还有哪些枚举值呢? 都代表什么意思? scope
有6个枚举值, 分别如下
compile
, 默认值。表示这个依赖的jar包需要在build、test、run的时候都在classPath中test
, 跑单测的时候会用到这个依赖包system
, 依赖本地jar包,不从maven仓库中获取。如果使用system scope,则在<dependency>
配置块中需要增加<systemPath>${basedir}\war\WEB-INF\lib\extDependency.jar</systemPath>
的说明。import
, 只会出现在dependencyManagement
部分,dependencyManagement
用于“声明”项目所需要的依赖(但不引用),主要为了解决maven没法做到多继承的场景,比如我的模块要继承多个父pom,这个是没法做到的。如果在一个project里边有100个module,每个module的版本都在一个parent pom里边维护的话,太混乱了。这时可以分开把不同类型的依赖包放到不同的"管理pom文件"中(<dependency>
的<type>
是pom)进行管理,各个子module分别去import这些pom,这个理念有点类似于interface
,区别于parent class
。providerd
, 跟compile很像,需要在build、test时在classPath下,而run的时候不需要,因为已经有容器提供这个包的服务。runtime
, 无需编译,但是该依赖的jar包出现在其他的classPath下,可以用于test,run服务
三、dependencies和dependencyManagement
要理解dependencyManagement
需要知道它出现的背景,在一个多模块的工程中,各模块可能会依赖相同的jar包,之前是在每个模块的pom.xml
中通过具体jar包的坐标来指定,但可能会造成不同版本之间jar包冲突导致的运行问题。
解决这个问题的方法就是在项目顶层pom.xml
文件中使用dependencyManagement
来声明那些可能用到的jar包以及其版本。子模块通过<parent>
标签来指定依赖顶层项目的pom
。子模块在写具体的dependencies
的时候,就可以不指定jar包的版本,默认会使用<parent>
中的dependencyManagement
中的版本,从而达到在一个项目中依赖的第三方jar包版本统一的目的。
四、dependency标签下参数
<dependency> <groupId></groupId> <artifactId></artifactId> <version></version> <type></type> <optional></optional> <scope></scope> <classifier></classifier> <systemPath></systemPath> <exclusions></exclusions> </dependency>
基本的groupId、artifactId、version在这里就不介绍了。
4.1 type
有时候我们引入某一个依赖时,必须指定type
,这是因为用于匹配dependency引用和dependencyManagement部分的最小信息集实际上是{groupId,artifactId,type,classifier}。在很多情况下,这些依赖关系将引用没有classifier的jar依赖。这允许我们将标识设置为{groupId,artifactId},因为type
的默认值是jar
,并且默认classifier
为null
。
type的值一般有jar、war、pom等,声明引入的依赖的类型。
4.2 classifier
Classifier可能是最容易被忽略的Maven特性,但它确实非常重要,我们也需要它来帮助规划坐标。设想这样一个情况,有一个jar项目,就说是 dog-cli-1.0.jar 吧,运行它用户就能在命令行上画一只小狗出来。现在用户的要求是希望你能提供一个zip包,里面不仅包含这个可运行的jar,还得包含源代码和文档,换句话说,这是比较正式的分发包。这个文件名应该是怎样的呢?dog-cli-1.0.zip?不够清楚,仅仅从扩展名很难分辨什么是Maven默认生成的构件,什么是额外配置生成分发包。如果能是dog-cli-1.0-dist.zip就最好了。这里的dist就是classifier,默认Maven只生成一个构件,我们称之为主构件,那当我们希望Maven生成其他附属构件的时候,就能用上classifier。常见的classifier还有如dog-cli-1.0-sources.jar表示源码包,dog-cli-1.0-javadoc.jar表示JavaDoc包等等。
classifier它表示在相同版本下针对不同的环境或者jdk使用的jar,如果配置了这个元素,则会将这个元素名在加在最后来查找相应的jar,例如:
上面的例子 <classifier>sources</classifier> <classifier>javadoc</classifier>
给相同的version,构建不同的环境使用依赖 <classifier>jdk17</classifier> <classifier>jdk18</classifier>
4.3 optional
当project-A 依赖project-B, project-B 依赖project-D时
<dependency> <groupId>sample.ProjectD</groupId> <artifactId>ProjectD</artifactId> <version>1.0-SNAPSHOT</version> <optional>true</optional> </dependency>
所以当project-B的<optional>
true</optional>
时, project-A中如果没有显式的引入project-D, 则project-A不依赖project-D, 即project-A可以自己选择是否依赖project-D
默认<optional>
的值为false, 及子项目必须依赖。
4.4 scope
上文讲到了,不再列出了
4.5 systemPath
当maven依赖本地而非repository中的jar包,sytemPath指明本地jar包路径,例如:
<dependency> <groupId>cn.ucloud.ufile</groupId> <artifactId>cn.ucloud.ufile</artifactId> <scope>system</scope> <version>0.1.1</version> <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/ufile_sdk.jar</systemPath> </dependency>
4.6 exclusions
依赖排除,就是有时候引入某一个依赖的时候,该依赖下有jar包冲突,可以排除掉,不引用该jar,例如:
<dependency> <groupId>cn.ucloud.ufile</groupId> <artifactId>cn.ucloud.ufile</artifactId> <scope>system</scope> <version>0.1.1</version> <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/ufile_sdk.jar</systemPath> <exclusions> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </exclusion> </exclusions> </dependency>