使用Apache httpcomponents时仅Tomcat运行抛出java.lang.NoSuchFieldError: INSTANCE
问题原因
这个java.lang.NoSuchFieldError: INSTANCE异常本质是Apache HttpComponents组件的版本不兼容冲突:
- 你本地独立测试时,类路径只有你指定的httpclient 4.5.13、httpcore 4.4.14版本,版本匹配所以运行正常
- 部署到Tomcat 9运行集成测试时,Tomcat自身
lib目录默认携带了低版本的httpcore、httpclient相关jar包,Tomcat的类加载机制默认优先加载服务器自带的jar包,导致高版本的httpclient调用了低版本不兼容的httpcore类,SSLConnectionSocketFactory类初始化时需要的INSTANCE静态字段在低版本httpcore中不存在,因此抛出异常。即使你自己的WEB-INF/lib下没有重复版本,也会因为服务器级别的类加载优先级出现该问题。
解决方案
你可以根据实际场景选择以下任意一种方案解决:
- 方案1:调整Web应用的类加载优先级,优先加载应用自带的jar包。在你的Web应用
META-INF目录下新建context.xml文件,添加如下配置即可:
<?xml version="1.0" encoding="UTF-8"?> <Context> <Loader delegate="false"/> </Context>
该配置会让Tomcat优先加载当前Web应用WEB-INF/lib下的jar包,跳过服务器自带的旧版本HttpComponents包。
- 方案2:替换Tomcat自带的HttpComponents包。找到Tomcat安装目录下的
lib文件夹,删除其中旧版本的httpcore-*.jar、httpclient-*.jar,把你使用的4.5.13版本httpclient、4.4.14版本httpcore的jar包放进去即可。如果你的Tomcat自身服务没有依赖HttpComponents的功能,也可以直接删除服务器自带的相关旧包。 - 方案3:类包重定位。如果无法修改Tomcat配置,你可以将用到的HttpComponents相关包的包名重命名(也叫shade打包),避免和Tomcat自带的包路径冲突,不过该方案需要借助构建工具完成,无构建工具的场景下不推荐使用。
你可以在报错代码前增加如下打印逻辑,验证类加载来源确认问题:
System.out.println("SSLConnectionSocketFactory加载路径:" + org.apache.http.conn.ssl.SSLConnectionSocketFactory.class.getProtectionDomain().getCodeSource().getLocation().getPath()); System.out.println("HttpCore相关类加载路径:" + org.apache.http.HttpVersion.class.getProtectionDomain().getCodeSource().getLocation().getPath());
内容的提问来源于stack exchange,提问作者KoMaBeLu
相关产品推荐
相关产品推荐

