Request mappings declared on an implemented interface: remaining gaps after #533
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- kotlin, spring-boot
- Domain
- developer-experience, tooling
Research direction
Start by comparing the cited entry points in modules/spring-web/src/main/kotlin/com/explyt/spring/web/ with PR #578, especially HandlerMethods.kt, SpringWebUtil.kt, and SpringWebProjectOpenApiGenerateAction.kt; check the MCP naming path in SpringMcpProvider.kt in PR #579. Run the proposed regression cases only after confirming the relevant behavior, including the Kotlin UAST indexing and JAX-RS fallback cases. Done means each confirmed gap has a focused test and the consumers behave consistently with the stated Spring annotation and resolver semantics.
Written by the indexing model from the issue text.
Description
Component
Spring Web — all supported IDEA lines (242–262); related MCP contract consumers in spring-ai on lines 252+.
Problem details
Follow-up checklist for request mappings declared on implemented interfaces after #533. These findings come from reading source code, not a runtime reproduction or a new regression-test run. This is not a claim that PR #578 is merged or that it fixes every consumer.
Verified refs: public/main 45206e9a6; web PR #578 public/imuromtsev/request-mapping-interface e7287788b; stacked MCP PR #579 public/imuromtsev/request-mapping-interface-mcp. Web paths below are relative to modules/spring-web/src/main/kotlin/com/explyt/spring/web/.
-
Run in Swagger still appears on the annotated interface declaration. Pre-existing on main:
providers/EndpointRunLineMarkerProvider.kt:43accepts an own mapping without a handler-class gate. In PR #578, :44–48 gates the containing class only when the method lacks its own mapping. The interface declaration remains accepted; the controller override now also receives the gutter. The interface is a mapping source, not a second concrete served handler. -
Different binding types on one inherited parameter are reported as multiple bindings, and MCP naming uses annotation-kind order rather than the selected argument resolver. In PR #578,
util/HandlerMethods.kt:72–78searches the parameter hierarchy separately for each requested annotation family.util/SpringWebUtil.kt:134–135,188–189therefore collects both an override's@RequestParam("q")and an interface's@RequestHeader("h")for the same position. Main's collectors read only the method's own parameters/annotations (:137–139,195–197); the cross-hierarchy duplication is exposed by PR #578, not an existing hierarchy merge on main.In PR #579 (not web PR #578),
modules/spring-ai/src/main/kotlin/com/explyt/spring/ai/mcp/SpringMcpProvider.kt:810–817,1938–1943searchesCOOKIE_VALUEbeforeREQUEST_PARAMacross the whole hierarchy. ThuswireNameOfcan select an inherited cookie name before an override's query name. The query collector itself already supplies its correct annotation name; the bad helper result does not necessarily become the final QUERY name in every consumer. For example, the multipart-name set (:710–718) also uses this helper and can disagree with the query collector.Important correction to the initial hypothesis: Spring does not discard every different binding annotation merely because the override has one. In Spring 6.2.19,
spring-webHandlerMethod.java:70extendsAnnotatedMethod.spring-coreannotation/AnnotatedMethod.java:295–329starts withanns = super.getParameterAnnotations(), doesmerged.addAll(Arrays.asList(anns)), and suppresses an inherited annotation only whenann.annotationType() == paramAnn.annotationType(). Therefore own annotations win for the same annotation type; different types remain present. Effective binding then depends on resolver order:spring-webmethod/support/HandlerMethodArgumentResolverComposite.java:130–139takes the first supporting resolver (break), and MVC'sRequestMappingHandlerAdapter.java:685–702registersRequestParamMethodArgumentResolverbefore header/cookie resolvers. With the default MVC resolver list, a String parameter with RequestParam plus RequestHeader/CookieValue binds as query — not twice. Do not implement a blanket "override annotation always wins across different kinds" rule; custom resolvers and other stacks need their actual order/uncertainty represented. -
Inherited Nullable is ignored in requiredness. Pre-existing own-parameter-only check on main
util/SpringWebUtil.kt:300–304; retained in PR #578 at :288–292. Newly inherited query/header collectors in PR #578 call this on the override (:139,193), so a Java override without its own@Nullablecan report required=true even when the interface parameter has it. Spring's merge above retains inherited Nullable when not overridden by the same type. -
Generate OpenAPI does not iterate inherited default methods. Pre-existing main
action/SpringWebProjectOpenApiGenerateAction.kt:64–66iteratesuClass.methods, not inherited mapped methods; in PR #578 this remains at :60–62. Changing interface type prefixes does not include an interface default handler which has no override in the controller. -
A non-overridden generic interface default handler can keep T as body type. Pre-existing main
util/SpringWebUtil.kt:307–323readsparam.type. In PR #578,util/HandlerMethods.kt:56–60retains the inherited method when no override exists, whileutil/SpringWebUtil.kt:295–307still readsparam.typewithout a controller-context substitutor. A concreteApi<Payload>controller with an inheritedcreate(@RequestBody T body)default method needsPayload, not rawT. This requires retaining the controller context even when the PSI method remains in the interface. -
Kotlin extension-handler parameter-index remapping needs verification. In PR #578,
inspections/SpringOmittedPathVariableParameterInspection.kt:33–39maps an inherited source parameter tohandler.uastParametersby the source UMethod's index. This positional remapping is new in PR #578; an extension receiver can change JVM/UAST parameter representation. This is a test/design risk, not a proven runtime bug: first construct a legal inherited extension-function handler and assert the receiver/parameter indexing before changing the implementation. The inspection still has an own-mapping gate at :49, so an entirely unannotated override is not sufficient to exercise this path. -
JAX-RS fallback and ownership need separate gates. In PR #578,
loader/JaxRsExchangeLoader.kt:83–89requires a concrete inheritor with a mapped Path, butimplementedInProject(:93–95) checks only non-abstractness. A concrete inheritor rejected by the Path filter can still suppress the declaration fallback. This asymmetry is introduced by PR #578; whether that fallback should remain visible must be made explicit in tests. Separately, concrete annotated resource classes in a dependency/API module can be listed by every applicable dependent module: mainloader/SpringWebEndpointsLoader.kt:48–49searchesmoduleWithDependenciesScope, and mainloader/JaxRsExchangeLoader.kt:58–66publishes each annotated containing class without owner filtering. In PR #578,JaxRsExchangeLoader.kt:79–80still immediately returns a concrete annotated class without testing module ownership. That cross-module concrete-resource behavior is pre-existing, not caused byimplementedInProject.
Steps to reproduce
Proposed regression cases, not executed:
- API interface with GetMapping and a RestController override: inspect gutters on both declarations.
- One String parameter: interface RequestHeader("h"), override RequestParam("q"); inspect the generated parameter list. Repeat with interface CookieValue("sid") and an override multipart RequestParam to exercise MCP naming/classification.
- Interface RequestParam plus Spring Nullable, plain non-null Java override parameter: inspect requiredness.
- An inherited default mapped method with no controller override: run Generate OpenAPI. Repeat with a generic default method and
Api<Payload>. - Legal Kotlin extension handler plus inherited named PathVariable: assert UAST receiver/parameter positions before running the inspection.
- JAX-RS abstract declaration with only a non-Path concrete inheritor; separately, a concrete annotated resource in an API module with two dependent modules: assert fallback policy and ownership.
Expected behavior
One effective binding per parameter according to Spring's merged annotations and resolver semantics; correct inherited nullability and controller-context generic types; inherited default handlers included in OpenAPI; actionable gutters on concrete handlers; inspection diagnostics anchored to the correct implementing parameter; consistent JAX-RS fallback/ownership policy.
Actual behavior
The consumers above retain own-method-only or per-annotation/per-module assumptions after the handler/mapping-source split. Items 1, 3, 4, 5 and concrete-resource ownership in 7 predate PR #578; cross-hierarchy duplication in 2, index remapping in 6 and fallback asymmetry in 7 are introduced/exposed by it. The wire-name lookup in 2 is in stacked PR #579. Items 6 and the fallback policy in 7 need discriminating tests before asserting a specific observed failure.
Additional context
Related: #533, PR #578 and PR #579 (merge together). This checklist is separate from #533's interface-versus-override endpoint anchor, and from the vendor dependency-scope follow-up. Duplicate searches: interface mappings, inherited parameters, Swagger interface, default OpenAPI; no separate remaining-gaps issue found. Framework statements above were checked against cached Spring 6.2.19 source jars; no application or Gradle tasks were run.
- Dominant language
- Kotlin
- Stars
- 163
- Forks
- 16
- Avg merge
- 6h 35m
- Merged PRs (30d)
- 128
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from explyt/spring-plugin
-
spring-mcp-tools
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
explyt/spring-plugin#591 ·
Maintainers usually reply within 1 day
-
in:spring-core native-link
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
explyt/spring-plugin#375 ·
Maintainers usually reply within 1 day
-
in:spring-web
Difficulty 3/5 1-2 days Newbie friendliness 52/100
explyt/spring-plugin#609 ·
Maintainers usually reply within 1 day
-
in:spring-web
Difficulty 3/5 Half a day Newbie friendliness 66/100
explyt/spring-plugin#607 ·
Maintainers usually reply within 1 day
-
in:spring-web
Difficulty 4/5 3-5 days Newbie friendliness 25/100
explyt/spring-plugin#606 ·
Maintainers usually reply within 1 day
All issues in explyt/spring-plugin
Similar issues
-
afk-ok area:data-quality importer size:S
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
enorm-labs/event-junkie#3027 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
rustyrazorblade/easy-db-lab#1003 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 74/100
mason-org/mason-registry#17366 ·
Maintainers usually reply within 1 day