最近在升级springmvc项目到springboot项目,原来用htmlunit写的模拟百度浏览器查询关键字功能,一直报错,异常信息也比较少:java.lang.NoSuchFieldException: sslcontext。
从信息上来看应该是java反射获取属性的时候找不见。最开始怀疑是因为版本升级引起的。在查看以后,将原来明显相关的三个包查看发现其中一个的版本确实不一致,于是通过dependency依赖直接替换为原版本,发现还不行,报错一直是:java.lang.NoSuchFieldException: sslcontext.
那么我们通过maven的试图看看htmlunit的jar自动引入的依赖是什么。
我们在pom文件中引入:
net.sourceforge.htmlunit
htmlunit
2.14
那么这个依赖做了什么:
从这个图中可以看出,htmlunit包会默认加载很多他自己的依赖,包括我用蓝框圈住的httpClient,我们之前看到的报错就来自这个包:
根据报错行:
// 百度搜索首页页面
HtmlPage htmlPage = webClient.getPage("https://www.baidu.com/gaoji/advanced.html");
慢慢的跟进去你就可以走到:HttpWebConnection这个类的getResponse方法
public WebResponse getResponse(final WebRequest request) throws IOException {
final URL url = request.getUrl();
final HttpClientBuilder builder = reconfigureHttpClientIfNeeded(getHttpClientBuilder());
HtmlUnitHttpClientBuilder.configureConnectionManager(builder);
HttpUriRequest httpMethod = null;
try {
try {
httpMethod = makeHttpMethod(request);
}
catch (final URISyntaxException e) {
throw new IOException("Unable to create URI from URL: " + url.toExternalForm()
+ " (reason: " + e.getMessage() + ")", e);
}
final HttpHost hostConfiguration = getHostConfiguration(request);
// setProxy(httpMethod, request);
final long startTime = System.currentTimeMillis();
HttpResponse httpResponse = null;
try {
httpResponse = builder.build().execute(hostConfiguration, httpMethod, httpContext_);
}
catch (final SSLPeerUnverifiedException s) {
// Try to use only SSLv3 instead
if (webClient_.getOptions().isUseInsecureSSL()) {
HtmlUnitSSLConnectionSocketFactory.setUseSSL3Only(httpContext_, true);
httpResponse = builder.build().execute(hostConfiguration, httpMethod);
}
else {
throw s;
}
}
catch (final Error e) {
// in case a StackOverflowError occurs while the connection is leased, it won't get released.
// Calling code may catch the StackOverflowError, but due to the leak, the httpClient_ may
// come out of connections and throw a ConnectionPoolTimeoutException.
// => best solution, discard the HttpClient instance.
synchronized (this) {
httpClientBuilder_ = null;
}
throw e;
}
final DownloadedContent downloadedBody = downloadResponseBody(httpResponse);
final long endTime = System.currentTimeMillis();
return makeWebResponse(httpResponse, request, downloadedBody, endTime - startTime);
}
finally {
if (httpMethod != null) {
onResponseGenerated(httpMethod);
}
}
}
重点在这一句:
HtmlUnitHttpClientBuilder.configureConnectionManager(builder);
去构建执行器的时候报错:
我红框标记的地方就是获取sslcontext的地方,而这个builder根据方法参数,我们可以查看到这个
HttpClientBuilder这个类中是否存在属性sslcontext。
如果你在报sslcontext属性没有,那你对应的类中应该是没有我框出来的这个属性的。这说明你的其他依赖或者当前依赖引入的包中的httpclient包中的HttpClientBuilder这个类对象不兼容。需要做替换,或者用htmlunit包默认引进的,需要把手动引入或因为其他依赖引入的包引起的问题解决掉。
我这里遇到的是4.5.2版本的httpclient里面没有这个属性所以报错,我回退到4.3.2版本,问题解决。
到这里基本上就完成了问题的排查。当然因为这个htmlunit还有其他多个依赖从图中可以看出,如果你项目中有其他的依赖jar和它的jar有相同的依赖,都有可能会引起这种问题。解决思路是相同的。