From 02a7f4af148f3f946b10903feb94dc3f51bb63cd Mon Sep 17 00:00:00 2001 From: Xiaoyumuxi <3075514079@qq.com> Date: Mon, 14 Sep 2026 14:52:08 +0800 Subject: [PATCH 1/4] fix: lazily initialize RpcClient in JDK proxy --- .../rpc/core/client/JdkProxyFactory.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java index 4e6d083..33d27c9 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/JdkProxyFactory.java @@ -14,16 +14,30 @@ public class JdkProxyFactory implements ProxyFactory { - private final RpcClient rpcClient; + private volatile RpcClient rpcClient; public JdkProxyFactory() { - this(new RpcClient()); + // SPI 扩展加载阶段保持轻量,不在构造时初始化注册中心和传输层。 } JdkProxyFactory(RpcClient rpcClient) { this.rpcClient = Objects.requireNonNull(rpcClient, "rpcClient"); } + private RpcClient getRpcClient() { + RpcClient client = rpcClient; + if (client == null) { + synchronized (this) { + client = rpcClient; + if (client == null) { + client = new RpcClient(); + rpcClient = client; + } + } + } + return client; + } + @Override @SuppressWarnings("unchecked") public T getProxy(Class clazz) { @@ -54,7 +68,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - CompletableFuture future = rpcClient.sendRequest(request, method.getReturnType()); + CompletableFuture future = getRpcClient().sendRequest(request, method.getReturnType()); // 如果业务接口声明的返回类型是异步的,直接返回 Future;否则阻塞等待结果 if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) { return future; From ba3e1f588bf0478f3f7457df3bfacb8b25edb19c Mon Sep 17 00:00:00 2001 From: Xiaoyumuxi <3075514079@qq.com> Date: Mon, 14 Sep 2026 14:52:22 +0800 Subject: [PATCH 2/4] fix: lazily initialize RpcClient in ByteBuddy proxy --- .../core/client/ByteBuddyProxyFactory.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java index cacbafa..7a9fcbd 100644 --- a/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java +++ b/rpc-core/src/main/java/com/xiaoyu/rpc/core/client/ByteBuddyProxyFactory.java @@ -11,14 +11,33 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.util.Objects; import java.util.concurrent.CompletableFuture; public class ByteBuddyProxyFactory implements ProxyFactory { - private final RpcClient rpcClient; + private volatile RpcClient rpcClient; public ByteBuddyProxyFactory() { - this.rpcClient = new RpcClient(); + // 与 JDK Proxy 一致:SPI 扩展加载阶段不初始化注册中心和传输层。 + } + + ByteBuddyProxyFactory(RpcClient rpcClient) { + this.rpcClient = Objects.requireNonNull(rpcClient, "rpcClient"); + } + + private RpcClient getRpcClient() { + RpcClient client = rpcClient; + if (client == null) { + synchronized (this) { + client = rpcClient; + if (client == null) { + client = new RpcClient(); + rpcClient = client; + } + } + } + return client; } @Override @@ -52,7 +71,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } RpcRequest request = builder.build(); - CompletableFuture future = rpcClient.sendRequest(request, method.getReturnType()); + CompletableFuture future = getRpcClient().sendRequest(request, method.getReturnType()); // 如果业务接口声明的返回类型是异步的,直接返回 Future;否则阻塞等待结果 if (CompletableFuture.class.isAssignableFrom(method.getReturnType())) { return future; From df91b556e09ff7999f0765c4f235277d2df50b19 Mon Sep 17 00:00:00 2001 From: Xiaoyumuxi <3075514079@qq.com> Date: Mon, 14 Sep 2026 14:52:43 +0800 Subject: [PATCH 3/4] test: keep proxy SPI loading independent from transport --- .../com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java index c238114..5da1e9d 100644 --- a/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java +++ b/rpc-core/src/test/java/com/xiaoyu/rpc/core/extension/ExtensionLoaderTest.java @@ -82,12 +82,13 @@ void testLoadBalancerExtensions() { void testProxyFactoryExtensions() { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ProxyFactory.class); - // jdk 代理实现不依赖网络与注册中心,单元测试里直接实例化即可 + // ProxyFactory 的 SPI 加载必须保持轻量:rpc-core 本身不包含具体 Transport 实现, + // 因此这里只实例化代理工厂,不能在构造阶段触发 RpcClient/注册中心/网络初始化。 assertNotNull(loader.getExtension("jdk"), "JDK proxy factory should be loaded"); + assertNotNull(loader.getExtension("bytebuddy"), "ByteBuddy proxy factory should be loaded"); - // bytebuddy 在当前实现里会进一步初始化 RpcClient(包含注册中心/传输层依赖), - // 这里仅校验其扩展声明已被正确加载,避免把单元测试耦合到外部环境 var extensions = loader.getSupportedExtensions(); + assertTrue(extensions.contains("jdk"), "Should contain 'jdk' extension"); assertTrue(extensions.contains("bytebuddy"), "Should contain 'bytebuddy' extension"); assertEquals(2, extensions.size(), "Should have exactly 2 proxy factory extensions"); } From 77f6d5fe6c4a40f98ae5df543ce232ad066e7202 Mon Sep 17 00:00:00 2001 From: Xiaoyumuxi <3075514079@qq.com> Date: Mon, 14 Sep 2026 14:53:11 +0800 Subject: [PATCH 4/4] ci: upgrade GitHub Actions runtime versions --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b6f813..1bf5779 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v6 with: java-version: '17' distribution: 'temurin' @@ -35,7 +35,7 @@ jobs: - name: Upload test results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-results path: | @@ -47,10 +47,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v6 with: java-version: '17' distribution: 'temurin'