以太坊,作为全球领先的智能合约平台,其强大的生态系统离不开各种与区块链交互的方式,JSON-RPC(远程过程调用)是以太坊节点(如 Geth、Parity 或 Nethermind)提供的一套标准 API,允许应用程序与区块链进行通信,对于 Java 开发者而言,掌握如何通过 JSON-RPC 与以太坊网络交互,是构建去中心化应用(DApp)、钱包、数据分析工具等项目的核心技能,本文将深入探讨如何使用 Java 与以太坊 JSON-RPC API 进行交互,涵盖从环境搭建到具体代码实现的完整流程。
理解以太坊 JSON-RPC API
以太坊 JSON-RPC API 是一个基于 HTTP 或 WebSocket 的接口,它定义了一系列方法,允许客户端执行各种操作,
- 查询区块链状态:获取最新区块号、区块详情、交易收据、账户余额、代码等。
- 发送交易:将新的交易(如转账、调用智能合约)发送到以太坊网络进行打包。
- 与智能合约交互:调用智能合约的读函数(
call)和写函数(transact)。 - 事件监听:订阅区块链上的特定事件(如新区块、新交易、智能合约事件)。
这些方法以 JSON 格式通过 HTTP POST 请求发送给以太坊节点,节点处理后将结果以 JSON 格式返回,获取当前区块号的 eth_blockNumber 方法,请求和响应如下:
请求:
{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}
响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x1234567" // 十六进制格式的区块号
}
Java 环境准备与依赖库
在 Java 中与以太坊 JSON-RPC 交互,核心任务是构造符合规范的 JSON 请求,发送 HTTP 请求,并解析 JSON 响应,虽然可以手动使用如 HttpURLConnection 和 org.json 或 Gson、Jackson 等库来实现,但使用成熟的以太坊 Java 库能大大简化开发,提高效率,目前最流行和功能最强大的库是 Web3j。
添加 Web3j 依赖:
如果你使用 Maven,在 pom.xml 中添加以下依赖:
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.8</version> <!-- 请使用最新版本 -->
</dependency>
Gradle 用户则添加:
implementation 'org.web3j:core:4.9.8' // 请使用最新版本
Web3j 封装了以太坊 JSON-RPC API 的所有方法,提供了类型安全的 Java 对象,并处理了序列化和反序列化的细节。
以太坊节点连接:
你需要一个正在运行的以太坊节点,并知道其 JSON-RPC 端点地址,这可以是:
- 本地节点:如果你在自己的机器上运行了 Geth 或 Parity,通常默认端口是
8545(HTTP) 或8546(HTTPS/WSS)。 - 远程节点服务:如 Infura、Alchemy、QuickNode 等,它们提供公共或私有的 RPC 端点,无需自己运行节点。</li>

使用 Web3j 进行交互实践
连接到以太坊节点:
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import java.io.IOException;
public class EthereumConnection {
public static void main(String[] args) {
// 替换为你的以太坊节点 RPC URL
String rpcUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID";
// 本地节点示例: String rpcUrl = "http://localhost:8545";
Web3j web3j = Web3j.build(new HttpService(rpcUrl));
try {
// 检查连接是否成功
String clientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
System.out.println("Connected to Ethereum client: " + clientVersion);
// 关闭连接
web3j.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
查询账户余额:
假设我们要查询某个以太坊地址的余额。
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;
import java.math.BigInteger;
public class GetBalance {
public static void main(String[] args) {
String rpcUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID";
Web3j web3j = Web3j.build(new HttpService(rpcUrl));
String address = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; // 示例地址
try {
EthGetBalance ethGetBalance = web3j.ethGetBalance(address, org.web3j.protocol.core.DefaultBlockParameterName.LATEST).send();
BigInteger balanceWei = ethGetBalance.getBalance();
// 将 Wei 转换为 Ether
String balanceEth = Convert.fromWei(balanceWei.toString(), Unit.ETHER).toPlainString();
System.out.println("Balance of " + address + " is: " + balanceEth + " ETH");
web3j.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
发送交易(转账 ETH):
发送交易比查询状态复杂,因为它需要私钥签名交易,并且需要支付 Gas 费。
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.transaction.Transaction;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;
import org.web3j.utils.Numeric;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.ExecutionException;
public class SendTransaction {
public static void main(String[] args) {
String rpcUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID";
Web3j web3j = Web3j.build(new HttpService(rpcUrl));
// 发送方地址和私钥(实际项目中务必安全存储私钥,不要硬编码!)
String fromAddress = "0xYourFromAddress";
String privateKey = "YOUR_PRIVATE_KEY"; // 危险!仅用于演示
// 接收方地址
String toAddress = "0xReceiverAddress";
// 转账金额(0.01 ETH)
BigDecimal amountInEth = new BigDecimal("0.01");
BigInteger amountInWei = Convert.toWei(amountInEth, Unit.ETHER).toBigIntegerExact();
try {
// 1. 获取当前 nonce
BigInteger nonce = web3j.ethGetTransactionCount(fromAddress, org.web3j.protocol.core.DefaultBlockParameterName.LATEST)
.send()
.getTransactionCount();
// 2. 创建交易对象
Transaction transaction = Transaction.createEtherTransaction(
fromAddress,
nonce,
BigInteger.valueOf(200000), // Gas Limit
BigInteger.valueOf(20 * 1000000000), // Gas Price (20 Gwei)
toAddress,
amountInWei
);
// 3. 签名交易
org.web3j.crypto.RawTransaction rawTransaction = org.web3j.crypto.RawTransaction.createTransaction(
nonce,
transaction.getGasPrice(),
transaction.getGasLimit(),
transaction.getTo(),
transaction.getValue()
);
org.web3j.crypto.Sign.SignatureData signature = org.web3j.crypto.Sign.signMessage(
rawTransaction, org.web3j.crypto.Credentials.create(privateKey).getEcKeyPair()
);
// 4. 发送签名后的交易
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(
org.web3j.crypto.TransactionEncoder.signMessage(rawTransaction, signature)
).send();
if (ethSendTransaction.getTransactionHash() != null) {
System.out.println("Transaction sent! Hash: " + ethSendTransaction.getTransactionHash());
} else {
System.out.println("Error sending transaction: " + ethSendTransaction.getError().getMessage());
}
web3j.shutdown();
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
注意: 实际项目中,私钥管理至关重要,应使用硬件钱包、密钥管理系统或环境变量等方式,严禁硬编码。
与智能合约交互:
Web3j 也提供了与智能合约