Master JSON-RPC with Spring Boot 3: Full Guide and Code Samples
This article introduces the JSON-RPC 2.0 protocol, demonstrates how to integrate the jsonrpc4j library into a Spring Boot 3.4.2 project, and provides step‑by‑step code examples for defining services, exposing them via beans or annotations, configuring various client proxies, handling errors, and implementing streaming socket communication.
Environment: Spring Boot 3.4.2.
1. Introduction
JSON‑RPC is a lightweight remote‑procedure‑call protocol that uses JSON as the data format. The current widely used version is 2.0, which adds error handling, batch requests, and notifications.
JSON‑RPC 2.0 Structure
Request fields:
jsonrpc: String, usually "2.0"
method: String, the name of the method to invoke
params: optional, an array or object with parameters
id: optional, a string, number or null to correlate the response
Response fields:
jsonrpc: String, usually "2.0"
result: the return value when the request succeeds
error: the error object when the request fails
id: must match the request id
Only one of result or error is returned.
2. Practical Example with jsonrpc4j
2.1 Dependency Management & Interface Definition
<code><dependency>
<groupId>com.github.briandilley.jsonrpc4j</groupId>
<artifactId>jsonrpc4j</artifactId>
<version>1.7</version>
</dependency></code>Current latest version is 1.7.
<code>public interface UserService {
User createUser(String userName, String firstName, String password);
User createUser(String userName, String password);
User findUserByUserName(String userName);
int getUserCount();
}
@Service
@Primary
public class UserServiceImpl implements UserService {
private static final List<User> USERS = new ArrayList<>();
public User createUser(String userName, String firstName, String password) {
User user = new User(userName, firstName, password);
USERS.add(user);
return user;
}
public User createUser(String userName, String password) {
return this.createUser(userName, null, password);
}
public User findUserByUserName(String userName) {
System.err.println("admin".equals(userName) ? 1 / 0 : "success");
return USERS.stream()
.filter(user -> user.userName().equals(userName))
.findFirst()
.orElse(null);
}
public int getUserCount() {
return USERS.size();
}
}</code>2.2 RPC Server Exposure
Method 1: JsonServiceExporter bean
<code>@Bean("/us")
JsonServiceExporter exporter(UserService userService, ObjectMapper objectMapper) {
JsonServiceExporter exporter = new JsonServiceExporter();
exporter.setServiceInterface(UserService.class);
exporter.setObjectMapper(objectMapper);
exporter.setService(userService);
return exporter;
}</code>Service is accessible at http://localhost:8080/us for any JSON‑RPC client.
Method 2: Annotation‑based discovery
<code>@JsonRpcService("/us")
public interface UserService {}
@AutoJsonRpcServiceImpl
@Service
@Primary
public class UserServiceImpl implements UserService {}
@Bean
AutoJsonRpcServiceImplExporter autoExporter() {
return new AutoJsonRpcServiceImplExporter();
}</code>2.3 RPC Client Invocation
Configuration 1: AutoJsonRpcClientProxyCreator
<code>@Bean
AutoJsonRpcClientProxyCreator proxyCreator() throws Exception {
AutoJsonRpcClientProxyCreator creator = new AutoJsonRpcClientProxyCreator();
creator.setBaseUrl(URI.create("http://localhost:8080").toURL());
creator.setScanPackage("com.pack.rpc.server");
return creator;
}</code>Configuration 2: JsonProxyFactoryBean
<code>@Bean
JsonProxyFactoryBean userServiceProxy() {
JsonProxyFactoryBean proxy = new JsonProxyFactoryBean();
proxy.setServiceUrl("http://localhost:8080/us");
proxy.setServiceInterface(UserService.class);
return proxy;
}</code>Configuration 3: JsonRpcHttpClient
<code>JsonRpcHttpClient client = new JsonRpcHttpClient(URI.create("http://localhost:8080/us").toURL());
User user = client.invoke("createUser", new Object[]{"Spring Boot3实战案例200讲", "Pack", "123456"}, User.class);
System.err.println(user);
UserService userService = ProxyUtil.createClientProxy(
ClientTest.class.getClassLoader(), UserService.class, client);
User user2 = userService.createUser("Pack", "xg");
System.err.println(user2);
</code>2.4 Testing Controller
<code>@RestController
@RequestMapping("/rpc")
public class RpcController {
private final UserService userService;
public RpcController(UserService userService) { this.userService = userService; }
@GetMapping("/create")
public ResponseEntity<User> createUser(String userName, String firstName, String password) {
return ResponseEntity.ok(userService.createUser(userName, firstName, password));
}
@GetMapping("/query")
public ResponseEntity<User> queryUser(String userName) {
return ResponseEntity.ok(userService.findUserByUserName(userName));
}
}
</code>2.5 Custom Error Handling
<code>@JsonRpcErrors({
@JsonRpcError(exception = Exception.class, code = -1, message = "服务发生异常")
})
User findUserByUserName(String userName);
</code>2.6 Streaming (Socket) Service
Server side
<code>JsonRpcServer server = new JsonRpcServer(new UserServiceImpl());
int maxThreads = 50;
int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);
StreamServer ss = new StreamServer(server, maxThreads, serverSocket);
ss.start();
</code>Client side
<code>Socket socket = new Socket(InetAddress.getLocalHost(), 8080);
OutputStream os = socket.getOutputStream();
Map<String, Object> data = Map.of(
"jsonrpc", "2.0",
"method", "createUser",
"params", new Object[]{"Spring Boot3实战案例200讲", "Pack", "111111"},
"id", "s-0001"
);
os.write(new ObjectMapper().writeValueAsBytes(data));
socket.shutdownOutput();
InputStream is = socket.getInputStream();
System.err.println(new String(is.readAllBytes()));
</code>The above examples show successful calls, error responses, and how to customize error messages.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.