htmlunit实现模拟浏览器请求报错sslcontext没有的解决思路

最近在升级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。

htmlunit实现模拟浏览器请求报错sslcontext没有的解决思路_第1张图片

如果你在报sslcontext属性没有,那你对应的类中应该是没有我框出来的这个属性的。这说明你的其他依赖或者当前依赖引入的包中的httpclient包中的HttpClientBuilder这个类对象不兼容。需要做替换,或者用htmlunit包默认引进的,需要把手动引入或因为其他依赖引入的包引起的问题解决掉。

我这里遇到的是4.5.2版本的httpclient里面没有这个属性所以报错,我回退到4.3.2版本,问题解决。

到这里基本上就完成了问题的排查。当然因为这个htmlunit还有其他多个依赖从图中可以看出,如果你项目中有其他的依赖jar和它的jar有相同的依赖,都有可能会引起这种问题。解决思路是相同的。 

你可能感兴趣的:(学习)