JBoss/WildFly14中使用ResteasyClient出现LinkageError求助
问题重现
你在WildFly14上运行代码时遇到了LinkageError,用普通HttpClient一切正常,但为了配置代理改用ResteasyClient就报错了。你的代码大致是这样的:
try { HttpClientBuilder clientBuilder = HttpClientBuilder.create(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new java.security.SecureRandom()); ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine((HttpClient)clientBuilder.build()); ResteasyClient client = new ResteasyClientBuilder() .httpEngine(engine) .sslContext(sslContext).hostnameVerifier((s1, s2) -> true) .build(); return client; } catch (Exception ex) { NetqosSharedLogger .error("Unable to configure HttpClient correctly, using fallback connection configuration", ex); }
错误栈核心信息:
Caused by: java.lang.LinkageError: loader constraint violation: when resolving method "org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.
(Lorg/apache/http/client/HttpClient;)V" the class loader (instance of org/jboss/modules/ModuleClassLoader) of the current class, com/ca/im/tmi/connector/agent/webservices/ClientUtils, and the class loader (instance of org/jboss/modules/ModuleClassLoader) for the method's defining class, org/jboss/resteasy/client/jaxrs/engines/ApacheHttpClient4Engine, have different Class objects for the type org/apache/http/client/HttpClient used in the signature
问题原因
这个错误本质是类加载器隔离导致的类型不匹配:
- WildFly用
ModuleClassLoader来隔离不同模块的类,自带的Resteasy模块(包含ApacheHttpClient4Engine)依赖的是WildFly自身模块里的org.apache.http.client.HttpClient类。 - 而你代码里用
HttpClientBuilder创建的HttpClient,是由你应用的类加载器加载的(来自WEB-INF/lib里的httpclient jar包)。 - 这两个
HttpClient虽然全类名一样,但属于不同类加载器加载的Class对象,JVM会认为它们是不同的类型,所以在调用ApacheHttpClient4Engine的构造方法时就触发了LinkageError。
解决方案
我给你三个可行的解决思路,按推荐优先级排序:
1. 用ResteasyClientBuilder原生方法配置代理(最推荐)
没必要手动创建ApacheHttpClient4Engine,ResteasyClientBuilder本身就支持代理配置,完全可以绕过类加载冲突:
try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new java.security.SecureRandom()); // 直接用ResteasyClientBuilder配置代理 ResteasyClient client = new ResteasyClientBuilder() .sslContext(sslContext) .hostnameVerifier((s1, s2) -> true) // 替换成你的代理地址、用户名和密码(不需要认证的话可以去掉后两个参数) .proxy(new URI("http://your-proxy-host:8080"), "proxy-username", "proxy-password") .build(); return client; } catch (Exception ex) { NetqosSharedLogger.error("Unable to configure HttpClient correctly, using fallback connection configuration", ex); return new ResteasyClientBuilder().build(); }
2. 让应用使用WildFly自带的HttpClient模块
如果你一定要自定义HttpClient,可以让应用依赖WildFly自带的版本,避免自己打包httpclient jar:
- 修改Maven依赖,将httpclient设为
provided:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <!-- 版本要和WildFly14自带的保持一致,WildFly14默认用的是4.5.5 --> <version>4.5.5</version> <scope>provided</scope> </dependency>
- 在应用的
WEB-INF/jboss-deployment-structure.xml里声明依赖WildFly的httpcomponents模块:
<jboss-deployment-structure> <deployment> <dependencies> <module name="org.apache.httpcomponents" export="true"/> <module name="org.jboss.resteasy.resteasy-client" export="true"/> </dependencies> </deployment> </jboss-deployment-structure>
这样你的代码里创建的HttpClient和Resteasy模块用的就是同一个类加载器加载的版本,就不会有类型冲突了。
3. 将HttpClient部署到WildFly模块(不推荐)
把httpclient的jar包放到WildFly的modules目录下,自定义一个模块让Resteasy依赖它。这种方式比较繁琐,需要修改WildFly的模块配置,除非有特殊需求,否则不建议用。
内容的提问来源于stack exchange,提问作者Sounak Saha

