This document explains every supported method registration style, how conflicts are resolved, and how parameters/results are bound.
Use on Spring-managed beans when jsonrpc.scan-annotated-methods=true.
import com.limehee.jsonrpc.core.JsonRpcMethod;
import com.limehee.jsonrpc.core.JsonRpcParam;
import org.springframework.stereotype.Service;
@Service
class UserRpcService {
@JsonRpcMethod("user.find")
public UserDto find(@JsonRpcParam("id") long id) {
return new UserDto(id, "lime");
}
@JsonRpcMethod
public String ping() {
return "pong";
}
record UserDto(long id, String name) {}
}Method name rule:
- explicit annotation value -> used as-is
- empty annotation value -> Java method name
import tools.jackson.databind.node.StringNode;
import com.limehee.jsonrpc.core.JsonRpcMethodRegistration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class RpcRegistrationConfig {
@Bean
JsonRpcMethodRegistration pingRegistration() {
return JsonRpcMethodRegistration.of("manual.ping", params -> StringNode.valueOf("pong-manual"));
}
}This style is explicit and works well for modular composition.
import com.limehee.jsonrpc.core.JsonRpcMethodRegistration;
import com.limehee.jsonrpc.core.JsonRpcTypedMethodHandlerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class TypedRegistrationConfig {
record UpperIn(String value) {}
record UpperOut(String value) {}
@Bean
JsonRpcMethodRegistration upperRegistration(JsonRpcTypedMethodHandlerFactory factory) {
return JsonRpcMethodRegistration.of(
"typed.upper",
factory.unary(UpperIn.class, in -> new UpperOut(in.value().toUpperCase()))
);
}
}Use this style when you want strict DTO-based mapping while still registering manually.
In Spring Boot auto-configuration, registration happens in two phases:
- During
JsonRpcDispatcherbean creation:- all
JsonRpcMethodRegistrationbeans are registered - registration uses
ObjectProvider.orderedStream() @Order/Orderedaffects order in this phase
- all
- After singletons are instantiated:
JsonRpcAnnotatedMethodRegistrarscans beans for@JsonRpcMethod- annotated methods are registered
Priority summary:
- manual registration phase runs first
- annotation phase runs second
Duplicate method names are controlled by jsonrpc.method-registration-conflict-policy.
- duplicate registration throws
- startup/runtime registration fails fast
- later registration replaces earlier one
- because annotation phase runs later, annotation can override manual registration for the same method name
Configuration:
jsonrpc:
method-registration-conflict-policy: REJECTAccepted request shapes include:
paramsomittedparams: nullparams: {}params: [](for typed no-arg adapters)
Non-empty parameters for no-arg methods produce -32602.
- entire
paramsnode is converted to the parameter type - conversion uses
JsonRpcParameterBinder(default Jackson-based)
Example:
@JsonRpcMethod("greet")
public String greet(GreetParams params) {
return "hello " + params.name();
}
record GreetParams(String name) {}Mode is chosen by request params shape:
- Object -> named binding
- Non-object -> positional binding (array expected)
Parameter name resolution order:
@JsonRpcParam("name")- reflection parameter name (requires
-parameters)
Missing key for any argument -> -32602.
Example:
@JsonRpcMethod("sum")
public int sum(@JsonRpcParam("left") int left, @JsonRpcParam("right") int right) {
return left + right;
}Request:
{"jsonrpc":"2.0","method":"sum","params":{"left":1,"right":2},"id":1}Rules:
paramsmust be an array- array size must exactly match method argument count
Example:
@JsonRpcMethod("sum")
public int sum(int left, int right) {
return left + right;
}Request:
{"jsonrpc":"2.0","method":"sum","params":[1,2],"id":1}Since binding/writing is Jackson-based, commonly supported types include:
- primitives and wrappers
- Java records
- POJOs/classes
- enums
List,Set,Map, nested collectionsJsonNode
Examples:
- parameter:
List<String> tags - parameter:
Map<String, Integer> counters - return:
List<MyDto> - return:
Map<String, Object>
- Return values are serialized via
JsonRpcResultWriter. - Default writer uses
ObjectMapper.valueToTree. - Returning
nullis valid and results in"result": null.
Method registration and method access are separate concerns:
- registration phase stores handlers
- access policy interceptor checks allowlist/denylist at call time
- denied methods map to
-32601 Method not foundfor non-disclosure behavior
- Use annotation style for simple service classes.
- Use manual/typed registration for reusable modules and deterministic wiring.
- Keep duplicate names disabled (
REJECT) unless you intentionally build layered overrides. - Prefer
@JsonRpcParamfor public APIs to avoid parameter-name compiler dependency issues.