在nodejs中使用Java方法
将Java类封装成JSON RPC 2.0服务,通过标准输入输出流发布。在nodejs中启动服务子进程,读写标准输入输出流,实现接口调用。
1. 方案
将现有Java库封装为JSON-RPC 2.0服务。假设现有Javalib.jar库,提供Javalib类和方法 String hello(String name)
。编写封装类,将Java方法封装成JSON-RPC 2.0接口。
代码1 将Java方法封装成JSON-RPC 2.0接口
private JSONObject serve(JSONObject request) {String method = request.getString("method");int id = request.getInt("id");JSONObject params = request.getJSONObject("params");JSONObject response = new JSONObject();response.put("jsonrpc", "2.0");response.put("id", id);JSONObject error = new JSONObject();switch (String.valueOf(method)) {case "hello": {String name = params.getString("name");Javalib javalib = new Javalib();String result = javalib.hello(name);response.put("result", result);break;}case "exit": response.put("result", "exit");running = false;break;default:error.put("code", -32601);error.put("messsage", "method not found");response.put("error", error);break;}return response; }
编写命令行程序,通过标准输入输出发布JSON-RPC 2.0服务。
代码2 通过标准输入输出发布JSON-RPC 2.0服务
private String serve(String jsonStr) {JSONObject request = new JSONObject(jsonStr);JSONObject response = serve(request);return response.toString(); }public static void main(String[] args) throws IOException {JsonRpcServer server = new JsonRpcServer();Scanner scanner = new Scanner(System.in);while (server.running) {String jsonStr = scanner.nextLine();System.out.println(server.serve(jsonStr));} }
在nodejs中启动服务程序,通过标准输入输出实现远程调用
代码3 在nodejs中通过标准输入输出实现远程调用
import { spawn } from 'child_process';class JsonRpcClient {constructor() {this.counter = 0;this.listeners = new Map();this.javaProcess = spawn('java', ['-cp', 'Javalib.jar;json-20250517.jar;.', 'JsonRpcServer']);this.javaProcess.stdout.on('data', (data) => {const jsonStr = data.toString();try {const { id, result } = JSON.parse(jsonStr);if (this.listeners.has(id)) {this.listeners.get(id)(jsonStr);this.listeners.delete(id);}} catch (err) {console.error('Failed to parse response:', err);}});this.javaProcess.stderr.on('data', (data) => {console.error('Java process error:', data.toString());});}async call(method, params = {}) {const id = ++this.counter;const request = {jsonrpc: "2.0",id,method,params,};const jsonStr = JSON.stringify(request);console.log(`发送 ${jsonStr}`);this.javaProcess.stdin.write(`${jsonStr}\n`);const { promise, resolve } = Promise.withResolvers();this.listeners.set(id, resolve);return promise;} }
上述步骤完成后,就可以在nodejs中通过JSON-RPC 2.0调用Java方法了。
代码4 通过JSON-RPC 2.0调用Java方法
(async () => {const client = new JsonRpcClient();try {let response = await client.call('hello', { name: 'Bob' })console.log(`接收 ${response}`);response = await client.call('non_exist');console.log(`接收 ${response}`);response = await client.call('exit');console.log(`接收 ${response}`);} catch (err) {console.error('RPC call failed:', err);} })();
2. 适用场景、局限和扩展
用于对性能要求不高的nodejs程序(如cli),快速对接现有Java库中的方法。如果对性能要求较高,或调用的Java方法需要支持复杂对象,可以使用node-java实现nodejs和Java的互操作。
由于使用了标准输入输出,上述方案对请求和应答对象的数据类型和大小存在一定限制。如果需要传输少量二进制数据,可以使用十六进制编码或base64编码。如果要传输大量数据,可以首先生成临时文件,将临时文件名作为参数传递。上述方案也可以扩展到“发布-订阅”模式。
3. 示例代码
代码5 Javalib.java
public class Javalib {public String hello(String name) {return "Hello " + name + " from Java!";} }
代码6 JsonRpcServer.java
// 需要下载org.json包。https://repo1.maven.org/maven2/org/json/json/20250517/json-20250517.jar import java.io.*; import java.util.Scanner; import java.util.Objects; import org.json.JSONObject; import org.json.JSONArray;public class JsonRpcServer {public boolean running = true;private JSONObject serve(JSONObject request) {String method = request.getString("method");int id = request.getInt("id");JSONObject params = request.getJSONObject("params");JSONObject response = new JSONObject();response.put("jsonrpc", "2.0");response.put("id", id);JSONObject error = new JSONObject();switch (String.valueOf(method)) {case "hello": {String name = params.getString("name");Javalib javalib = new Javalib();String result = javalib.hello(name);response.put("result", result);break;}case "exit": response.put("result", "exit");running = false;break;default:error.put("code", -32601);error.put("messsage", "method not found");response.put("error", error);break;}return response;}private String serve(String jsonStr) {JSONObject request = new JSONObject(jsonStr);JSONObject response = serve(request);return response.toString();}public static void main(String[] args) throws IOException {JsonRpcServer server = new JsonRpcServer();Scanner scanner = new Scanner(System.in);while (server.running) {String jsonStr = scanner.nextLine();System.out.println(server.serve(jsonStr));}} }
代码7 app.js
import { spawn } from 'child_process';class JsonRpcClient {constructor() {this.counter = 0;this.listeners = new Map();this.javaProcess = spawn('java', ['-cp', 'Javalib.jar;json-20250517.jar;.', 'JsonRpcServer']);this.javaProcess.stdout.on('data', (data) => {const jsonStr = data.toString();try {const { id, result } = JSON.parse(jsonStr);if (this.listeners.has(id)) {this.listeners.get(id)(jsonStr);this.listeners.delete(id);}} catch (err) {console.error('Failed to parse response:', err);}});this.javaProcess.stderr.on('data', (data) => {console.error('Java process error:', data.toString());});}async call(method, params = {}) {const id = ++this.counter;const request = {jsonrpc: "2.0",id,method,params,};const jsonStr = JSON.stringify(request);console.log(`发送 ${jsonStr}`);this.javaProcess.stdin.write(`${jsonStr}\n`);const { promise, resolve } = Promise.withResolvers();this.listeners.set(id, resolve);return promise;} }(async () => {const client = new JsonRpcClient();try {let response = await client.call('hello', { name: 'Bob' })console.log(`接收 ${response}`);response = await client.call('non_exist');console.log(`接收 ${response}`);response = await client.call('exit');console.log(`接收 ${response}`);} catch (err) {console.error('RPC call failed:', err);} })();
代码8 测试输入input.text
{"jsonrpc": "2.0", "id": 1, "method": "hello", "params": {"name": "Alex"}} {"jsonrpc": "2.0", "id": 1, "method": "exit", "params": {}}
代码9 测试脚本
# 编译Javalib javac Javalib.java && jar cvf Javalib.jar Javalib.class# 编译JsonRpcServer javac -cp "Javalib.jar;json-20250517.jar" JsonRpcServer.java# 测试JsonRpcServer gc input.txt | java -cp "Javalib.jar;json-20250517.jar;." JsonRpcServer # 测试app.js node app.js
4. 附录:JSON-RPC 2.0简介
JSON-RPC 2.0是一种轻量级、无状态的远程过程调用(RPC)协议,使用JSON作为数据交换格式。
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
jsonrpc | string | 是 | 固定为 "2.0" |
method | string | 是 | 方法名 |
params | array/object | 否 | 参数 |
id | string/number | 否 | 请求标识符 |
代码10 请求对象示例
{"jsonrpc": "2.0","method": "subtract","params": [42, 23],"id": 1 }
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
jsonrpc | string | 是 | 固定为 "2.0" |
result | any | 否 | 结果 |
error | 错误对象 | 否 | 错误信息 |
id | string/number | 是 | 与请求标识符一致 |
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | number | 是 | 错误码 |
message | string | 是 | 错误简介 |
data | any | 否 | 详细信息 |
错误码 | 说明 | |
---|---|---|
-32700 | Parse error | 语法解析错误 |
-32600 | Invalid Request | 无效请求 |
-32601 | Method not found | 找不到方法 |
-32602 | Invalid params | 无效的参数 |
-32603 | Internal error | 内部错误 |
-32000 到 -32099 | Server error | 服务端错误 |
语言 | 库/框架 |
---|---|
JavaScript | json-rpc-protocol |
Python | jsonrpcserver |
Go | jsonrpc2 |
Java | jsonrpc4j |
5. 参考资料
- JSON-RPC 2.0 Specification
- (译) JSON-RPC 2.0 规范(中文版) - wiki . leozvc