How Dynamic Proxies Power RPC Calls in Java

This article explains the core principle of RPC using dynamic proxies, showing how interfaces are proxied at runtime, the code generation process, and compares JDK, Javassist, and Byte Buddy proxy implementations with practical examples.

JavaEdge
JavaEdge
JavaEdge
How Dynamic Proxies Power RPC Calls in Java

Overview

Remote Procedure Call (RPC) abstracts remote service invocation as if it were a local method call. The key technique is dynamic proxy generation, which creates a proxy class for an interface at runtime, intercepts method calls, and injects remote invocation logic without modifying business code.

1. The Magic of Remote Calls

Developers depend on an interface provided by the service provider, added to the client project via Maven or similar tools. When the client injects the interface, the actual object bound at runtime is a dynamically generated proxy. Invoking a method on the interface triggers the proxy, which forwards the call to remote logic.

The proxy hides networking details, giving a seamless local‑like experience.

RPC call flow diagram
RPC call flow diagram

2. Implementation Details

package com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.v1;

public interface Hello {
    String say();
}
package com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.v1;

public class RealHello {
    public String invoke() {
        return "i'm proxy";
    }
}
package com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.v1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKProxy implements InvocationHandler {
    private Object target;
    public JDKProxy(Object target) { this.target = target; }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        return ((RealHello) target).invoke();
    }
}
package com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.v1;

import org.azeckoski.reflectutils.ClassLoaderUtils;
import java.lang.reflect.Proxy;

public class TestProxy {
    public static void main(String[] args) {
        JDKProxy proxy = new JDKProxy(new RealHello());
        ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Hello test = (Hello) Proxy.newProxyInstance(classLoader,
                new Class[]{Hello.class}, proxy);
        System.out.println(test.say());
    }
}

The generated $Proxy0 class implements Hello and forwards say() to JDKProxy.invoke(). The bytecode can be saved by setting the system property sun.misc.ProxyGenerator.saveGeneratedFiles=true, which stores the class under com/sun/proxy in the project root.

3. Proxy Generation Options

3.1 JDK Default Proxy

Works only for interfaces because the proxy class extends java.lang.reflect.Proxy. It relies on reflection for method dispatch, which may impact performance.

3.2 Javassist

Manipulates bytecode directly to create a proxy class, avoiding reflection and offering better performance. The generated CtClass becomes immutable after creation, preventing re‑generation errors.

3.3 Byte Buddy

Modern library used by frameworks like Spring and Jackson. Provides a fluent API, higher readability, and faster generated proxies compared to Javassist.

All three differ mainly in how they generate the proxy class and invoke the target method, leading to varying performance characteristics.

4. Summary

Proxy generation speed, bytecode size, and runtime overhead affect overall performance; smaller bytecode consumes fewer resources.

Since proxies intercept every interface method call, the generated proxy must execute efficiently.

Usability factors such as API clarity, community activity, and dependency complexity also influence framework choice.

FAQ

What if dynamic proxies are unavailable? Use static proxies, manually implementing each method and adding the same interception logic, which becomes cumbersome when many services need proxying.

How else can RPC be achieved? Define a common message ID and structure (e.g., protobuf) for both sides to exchange data.

References:

https://www.baeldung.com/jdk-com-sun-proxy

https://github.com/wangzheng0822/codedesign/tree/master/com/xzg/cd/rpc

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendjavaRPCDynamic ProxyJavassistJDK ProxyByte Buddy
JavaEdge
Written by

JavaEdge

First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.