Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Request mappings declared on an implemented interface: remaining gaps after #533

Open
#582 0 comments 0 reactions 0 assignees View on GitHub

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

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

in:spring-web spring-mcp-tools
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/.

  1. Run in Swagger still appears on the annotated interface declaration. Pre-existing on main: providers/EndpointRunLineMarkerProvider.kt:43 accepts 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.

  2. 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–78 searches the parameter hierarchy separately for each requested annotation family. util/SpringWebUtil.kt:134–135,188–189 therefore 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–1943 searches COOKIE_VALUE before REQUEST_PARAM across the whole hierarchy. Thus wireNameOf can 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-web HandlerMethod.java:70 extends AnnotatedMethod. spring-core annotation/AnnotatedMethod.java:295–329 starts with anns = super.getParameterAnnotations(), does merged.addAll(Arrays.asList(anns)), and suppresses an inherited annotation only when ann.annotationType() == paramAnn.annotationType(). Therefore own annotations win for the same annotation type; different types remain present. Effective binding then depends on resolver order: spring-web method/support/HandlerMethodArgumentResolverComposite.java:130–139 takes the first supporting resolver (break), and MVC's RequestMappingHandlerAdapter.java:685–702 registers RequestParamMethodArgumentResolver before 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.

  3. 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 @Nullable can report required=true even when the interface parameter has it. Spring's merge above retains inherited Nullable when not overridden by the same type.

  4. Generate OpenAPI does not iterate inherited default methods. Pre-existing main action/SpringWebProjectOpenApiGenerateAction.kt:64–66 iterates uClass.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.

  5. A non-overridden generic interface default handler can keep T as body type. Pre-existing main util/SpringWebUtil.kt:307–323 reads param.type. In PR #578, util/HandlerMethods.kt:56–60 retains the inherited method when no override exists, while util/SpringWebUtil.kt:295–307 still reads param.type without a controller-context substitutor. A concrete Api<Payload> controller with an inherited create(@RequestBody T body) default method needs Payload, not raw T. This requires retaining the controller context even when the PSI method remains in the interface.

  6. Kotlin extension-handler parameter-index remapping needs verification. In PR #578, inspections/SpringOmittedPathVariableParameterInspection.kt:33–39 maps an inherited source parameter to handler.uastParameters by 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.

  7. JAX-RS fallback and ownership need separate gates. In PR #578, loader/JaxRsExchangeLoader.kt:83–89 requires a concrete inheritor with a mapped Path, but implementedInProject (: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: main loader/SpringWebEndpointsLoader.kt:48–49 searches moduleWithDependenciesScope, and main loader/JaxRsExchangeLoader.kt:58–66 publishes each annotated containing class without owner filtering. In PR #578, JaxRsExchangeLoader.kt:79–80 still immediately returns a concrete annotated class without testing module ownership. That cross-module concrete-resource behavior is pre-existing, not caused by implementedInProject.

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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from explyt/spring-plugin

All issues in explyt/spring-plugin

Similar issues

More Kotlin issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.