最近以太坊的分叉,新增了一些字段,原来使用的是老版本 web3j-spring-boot-starter
<groupId>org.web3j</groupId> <artifactId>web3j-spring-boot-starter</artifactId> <version>1.6.0</version>
现在不得不更新web3j到最新版本包4.8.7,获取一些新的字段信息。
但是由于web3j的版本和spark.2.4.8的版本中okhttp3一直有冲突。
如果是开发spring项目,倒是可以正常使用,但是spark中okhttp3的版本要是升级也报错,最后查到如下方法,可以解决。
就是通过打包插件重新打包后修改包名解决,所以该方法也可以用于类似不兼容情况。
1. 新建项目myokhttp,pom文件设置如下:
(这里使用插件maven-shade-plugin的relocation来重新更新包名,不需要写任何程序,只需要添加依赖)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my_squareup.okhttp3</groupId> <artifactId>myokhttp</artifactId> <version>1.0.0</version> <properties> <okhttp.version>4.9.1</okhttp.version> </properties> <dependencies> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>logging-interceptor</artifactId> <version>${okhttp.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <relocation> <pattern>okhttp3</pattern> <shadedPattern>myokhttp3</shadedPattern> </relocation> </relocations> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/maven/**</exclude> </excludes> </filter> </filters> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
2. 打包程序后生成myokhttp-1.0.0.jar包文件,使用jd-gui.exe反编译查看,包名已经修改为myokhttp3开头。
3. 将包更新到我们的maven仓库中。
@echo off set jarFilePath=D:\tool\batchMaven\jar\myokhttp-1.0.0.jar set groupId=my_squareup.okhttp3 set artifactId=myokhttp set version=1.0.0 set s=mvn install:install-file -Dfile=%jarFilePath% -DgroupId=%groupId% -DartifactId=%artifactId% -Dversion=%version% -Dpackaging=jar echo %s% call %s% echo 执行完毕 pause
4. 然后在需要的项目中将web3j中的okhttp3包屏蔽,然后添加我们自定义的包的依赖。
<dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>4.8.7</version> <exclusions> <exclusion> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>my_squareup.okhttp3</groupId> <artifactId>myokhttp</artifactId> <version>1.0.0</version> </dependency>
5. 修改项目中使用到的okhttp3的地方。
1)这里还需要重新写一个HttpServer,从web3j中直接拷贝过来,把引用到okhttp3的地方修改为myokhttp3
/* * Copyright 2019 Web3 Labs Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package com.my.blockchain.http; import myokhttp3.*; import myokhttp3.logging.HttpLoggingInterceptor; import okio.Buffer; import okio.BufferedSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.web3j.protocol.Service; import org.web3j.protocol.exceptions.ClientConnectionException; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import static myokhttp3.ConnectionSpec.CLEARTEXT; /** HTTP implementation of our services API. */ public class HttpService extends Service { /** Copied from {@link ConnectionSpec#APPROVED_CIPHER_SUITES}. */ @SuppressWarnings("JavadocReference") private static final CipherSuite[] INFURA_CIPHER_SUITES = new CipherSuite[] { CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. // We'll // continue to include them until better suites are commonly available. For example, // none // of the better cipher suites listed above shipped with Android 4.4 or Java 7. CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // Additional INFURA CipherSuites CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256 }; private static final ConnectionSpec INFURA_CIPHER_SUITE_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .cipherSuites(INFURA_CIPHER_SUITES) .build(); /** The list of {@link ConnectionSpec} instances used by the connection. */ private static final List<ConnectionSpec> CONNECTION_SPEC_LIST = Arrays.asList(INFURA_CIPHER_SUITE_SPEC, CLEARTEXT); public static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); public static final String DEFAULT_URL = "http://localhost:8545/"; private static final Logger log = LoggerFactory.getLogger(HttpService.class); private OkHttpClient httpClient; private final String url; private final boolean includeRawResponse; private HashMap<String, String> headers = new HashMap<>(); public HttpService(String url, OkHttpClient httpClient, boolean includeRawResponses) { super(includeRawResponses); this.url = url; this.httpClient = httpClient; this.includeRawResponse = includeRawResponses; } public HttpService(OkHttpClient httpClient, boolean includeRawResponses) { this(DEFAULT_URL, httpClient, includeRawResponses); } public HttpService(String url, OkHttpClient httpClient) { this(url, httpClient, false); } public HttpService(String url) { this(url, createOkHttpClient()); } public HttpService(String url, boolean includeRawResponse) { this(url, createOkHttpClient(), includeRawResponse); } public HttpService(OkHttpClient httpClient) { this(DEFAULT_URL, httpClient); } public HttpService(boolean includeRawResponse) { this(DEFAULT_URL, includeRawResponse); } public HttpService() { this(DEFAULT_URL); } public static OkHttpClient.Builder getOkHttpClientBuilder() { final OkHttpClient.Builder builder = new OkHttpClient.Builder().connectionSpecs(CONNECTION_SPEC_LIST); configureLogging(builder); return builder; } private static OkHttpClient createOkHttpClient() { return getOkHttpClientBuilder().build(); } private static void configureLogging(OkHttpClient.Builder builder) { if (log.isDebugEnabled()) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(log::debug); logging.setLevel(HttpLoggingInterceptor.Level.BODY); builder.addInterceptor(logging); } } @Override protected InputStream performIO(String request) throws IOException { RequestBody requestBody = RequestBody.create(request, JSON_MEDIA_TYPE); Headers headers = buildHeaders(); myokhttp3.Request httpRequest = new myokhttp3.Request.Builder().url(url).headers(headers).post(requestBody).build(); try (myokhttp3.Response response = httpClient.newCall(httpRequest).execute()) { processHeaders(response.headers()); ResponseBody responseBody = response.body(); if (response.isSuccessful()) { if (responseBody != null) { return buildInputStream(responseBody); } else { return null; } } else { int code = response.code(); String text = responseBody == null ? "N/A" : responseBody.string(); throw new ClientConnectionException( "Invalid response received: " + code + "; " + text); } } } protected void processHeaders(Headers headers) { // Default implementation is empty } private InputStream buildInputStream(ResponseBody responseBody) throws IOException { if (includeRawResponse) { // we have to buffer the entire input payload, so that after processing // it can be re-read and used to populate the rawResponse field. BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); // Buffer the entire body Buffer buffer = source.getBuffer(); long size = buffer.size(); if (size > Integer.MAX_VALUE) { throw new UnsupportedOperationException( "Non-integer input buffer size specified: " + size); } int bufferSize = (int) size; InputStream inputStream = responseBody.byteStream(); BufferedInputStream bufferedinputStream = new BufferedInputStream(inputStream, bufferSize); bufferedinputStream.mark(inputStream.available()); return bufferedinputStream; } else { return new ByteArrayInputStream(responseBody.bytes()); } } private Headers buildHeaders() { return Headers.of(headers); } public void addHeader(String key, String value) { headers.put(key, value); } public void addHeaders(Map<String, String> headersToAdd) { headers.putAll(headersToAdd); } public HashMap<String, String> getHeaders() { return headers; } public String getUrl() { return url; } @Override public void close() throws IOException {} }
2)扩展web3j,新建Web3jExtend (由于web3j的查询功能有限,还需要查询额外的接口,这里做个扩展)
/** * @Author: KingWang * @Date: 2021/8/16 * @Desc: 补充web3j没有的接口功能 **/ public class Web3jExtend extends JsonRpc2_0Web3j implements Web3j { public Web3jExtend(Web3jService web3jService) { super(web3jService); } //这里写扩展的功能 public Request<?, NetVersion> test() { return new Request<>( "my_test", Collections.<String>emptyList(), web3jService, NetVersion.class); } }
3)建立web3j的工具类,工具类中使用的都是myokhttp类 (scala)
/** * @Author: KingWang * @Date: 2021/7/19 * @Desc: web3j的工具类 **/ object Web3jUtil { def getWeb3j:Web3jExtend = { val pool: ConnectionPool = new ConnectionPool(500, 5L, TimeUnit.MINUTES) val httpClient = new OkHttpClient().newBuilder().connectionPool(pool).build() val httpService = new HttpService(APIConstant.ethClientAddress, httpClient, false) new Web3jExtend(httpService) } }
6. 测试
def getBlock(blockNumber:String) = { val web3j = Web3jUtil.getWeb3j val defaultBlockParameter = new DefaultBlockParameterNumber(new BigInteger(blockNumber)) val ethBlock =web3j.ethGetBlockByNumber(defaultBlockParameter,true).send() val block = ethBlock.getBlock println(JSON.toJSONString(block,SerializerFeature.WriteMapNullValue)) }