Skip to content

fix: deduplicate methods in JavassistProxy to prevent DuplicateMemberException#1554

Open
sunhailin-Leo wants to merge 1 commit intomasterfrom
fix/javassist-duplicate-method
Open

fix: deduplicate methods in JavassistProxy to prevent DuplicateMemberException#1554
sunhailin-Leo wants to merge 1 commit intomasterfrom
fix/javassist-duplicate-method

Conversation

@sunhailin-Leo
Copy link
Collaborator

@sunhailin-Leo sunhailin-Leo commented Mar 24, 2026

What problem does this PR solve?

Fixes #1384

Root Cause

When a service interface extends multiple parent interfaces that declare the same method signature (e.g. both SubInterface1 and SubInterface2 declare String getType()), interfaceClass.getMethods() returns duplicate Method objects — one for each parent interface.

JavassistProxy.createMethod() previously iterated over all returned methods without any deduplication, causing Javassist to call mCtc.addMethod() with the same method signature twice, which throws:

javassist.bytecode.DuplicateMemberException: duplicate method: getType in HelloService_proxy_0

Switching to ByteBuddy proxy worked as a workaround because ByteBuddy handles deduplication internally.

Solution

Introduce addedMethodSignatures (HashSet<String>) in createMethod() to track method signatures in the format methodName(paramType1,paramType2,...). Before processing each method, check if its signature has already been seen; if so, skip it. This ensures each unique method signature is only added once to the generated proxy class.

Set<String> addedMethodSignatures = new HashSet<String>();
for (Method m : methodAry) {
    // ...
    if (!addedMethodSignatures.add(buildMethodSignature(m))) {
        continue; // skip duplicate method from multiple parent interfaces
    }
    mi++;
    // ... generate method body
}

Reproduce

public interface HelloService extends SubInterface1, SubInterface2 {
    String sayHello(String string);
}

interface SubInterface1 {
    String getType();   // same signature as SubInterface2.getType()
    String someMethod1();
}

interface SubInterface2 {
    String getType();   // duplicate!
    String someMethod2();
}

Before fix: throws SofaRpcRuntimeException wrapping DuplicateMemberException
After fix: proxy generated successfully

Changes

  • core-impl/proxy/src/main/java/com/alipay/sofa/rpc/proxy/javassist/JavassistProxy.java
    • Added import java.util.HashSet and import java.util.Set
    • Added addedMethodSignatures set in createMethod() for deduplication
    • Added private method buildMethodSignature(Method)

Summary by CodeRabbit

  • Bug Fixes
    • Fixed proxy generation failures that occurred when interfaces inherited identical methods from multiple parent interfaces, improving stability when working with complex interface hierarchies.

…Exception (#1384)

When an interface extends multiple parent interfaces that declare the
same method signature, interfaceClass.getMethods() returns duplicate
Method objects. JavassistProxy.createMethod() previously added all of
them without deduplication, causing Javassist to throw
DuplicateMemberException when attempting to add the same method twice
to the generated proxy class.

Fix: introduce addedMethodSignatures (HashSet<String>) to track method
signatures in the format 'methodName(paramType1,paramType2,...)'.
Before processing each method, check if its signature has already been
seen; if so, skip it. This ensures each unique method signature is
only added once to the proxy class, regardless of how many parent
interfaces declare it.

Fixes #1384
@sofastack-cla sofastack-cla bot added bug Something isn't working cla:yes CLA is ok size/M labels Mar 24, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eed56af6-f6d7-4ced-8441-973f8c722905

📥 Commits

Reviewing files that changed from the base of the PR and between ec810c2 and bf83cfa.

📒 Files selected for processing (1)
  • core-impl/proxy/src/main/java/com/alipay/sofa/rpc/proxy/javassist/JavassistProxy.java

📝 Walkthrough

Walkthrough

This change adds method-signature deduplication to Javassist proxy generation. When interfaces inherit identical methods from multiple parent interfaces, the proxy generator now tracks method signatures in a Set and skips duplicates, preventing Javassist from throwing DuplicateMemberException.

Changes

Cohort / File(s) Summary
Javassist Proxy Method Deduplication
core-impl/proxy/src/main/java/com/alipay/sofa/rpc/proxy/javassist/JavassistProxy.java
Added Set-based tracking of method signatures during proxy method generation to prevent duplicate method additions when interfaces inherit the same method from multiple parent interfaces. Introduced private helper buildMethodSignature(Method) that constructs signature strings from method name and parameter type class names.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A duplicate method tried twice to appear,
But our Set said "no, one version here!"
Javassist grins, no exception in sight,
Multiple inheritance now working just right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: deduplicating methods in JavassistProxy to fix DuplicateMemberException.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #1384 by adding method signature deduplication to prevent DuplicateMemberException when interfaces inherit identical methods.
Out of Scope Changes check ✅ Passed All changes are within scope, focusing solely on method deduplication in JavassistProxy to resolve the duplicate method issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/javassist-duplicate-method

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Javassist-based proxy generation when service interfaces inherit duplicate method signatures from multiple parent interfaces (issue #1384), preventing DuplicateMemberException during proxy class creation.

Changes:

  • Add a Set to deduplicate methods while iterating interfaceClass.getMethods() in createMethod().
  • Introduce a helper buildMethodSignature(Method) used as the deduplication key.
  • Add supporting imports (HashSet, Set) and inline documentation referencing #1384.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +317
private String buildMethodSignature(Method method) {
StringBuilder signature = new StringBuilder(method.getName());
signature.append('(');
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
signature.append(',');
}
signature.append(parameterTypes[i].getName());
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dedup key in buildMethodSignature() ignores the return type, but JVM/Javassist method uniqueness is based on name + full descriptor (including return type). With covariant return types (or compiler bridge methods), two methods can legally share the same name/parameter types but have different return types; this dedup would incorrectly drop one and may leave the generated proxy missing a required interface method or with the wrong return type. Include the return type in the signature (or derive the JVM descriptor) so only true duplicates are skipped.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +173
// Track method signatures to skip duplicate methods from multiple parent interfaces.
// When an interface extends multiple parent interfaces that declare the same method,
// interfaceClass.getMethods() returns duplicates, causing Javassist to throw
// DuplicateMemberException when adding the same method twice.
// See: https://github.com/sofastack/sofa-rpc/issues/1384
Set<String> addedMethodSignatures = new HashSet<String>();
for (Method m : methodAry) {
mi++;
if (Modifier.isNative(m.getModifiers()) || Modifier.isFinal(m.getModifiers()) ||
Modifier.isStatic(m.getModifiers())) {
continue;
}
// Skip duplicate methods inherited from multiple parent interfaces
if (!addedMethodSignatures.add(buildMethodSignature(m))) {
continue;
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change adds new behavior (skipping duplicate inherited methods) but there’s no test exercising the failure mode from #1384 (diamond interface inheritance with duplicate method signatures). Since this module already has JavassistProxyTest, consider adding a test interface that extends two parents declaring the same method and assert getProxy() succeeds (and the method invocation is routable) to prevent regressions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cla:yes CLA is ok size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Javassist does not handle interface duplication method correctly

2 participants