Spring MVC Annotated Controller Interface with @PathVariable

Apparently, when a request pattern is mapped to a method via the @RequestMapping annotation, it is mapped to to the concrete method implementation. So a request that matches the declaration will invoke GoalServiceImpl.removeGoal() directly rather than the method that originally declared the @RequestMapping ie GoalService.removeGoal().

Since an annotation on an interface, interface method, or interface method parameter does not carry over to the implementation there is no way for Spring MVC to recognize this as a @PathVariable unless the implementing class declares it explicitly. Without it, any AOP advice that targets @PathVariable parameters will not be executed.


Recently I had the same problem. Following has worked for me:

public class GoalServiceImpl implements GoalService {
    ...
    public void removeGoal(@PathVariableString id) {
    }
}

It works in newer version of Spring.

import org.springframework.web.bind.annotation.RequestMapping;
public interface TestApi {
    @RequestMapping("/test")
    public String test();
}

Implement the interface in the Controller

@RestController
@Slf4j
public class TestApiController implements TestApi {

    @Override
    public String test() {
        log.info("In Test");
        return "Value";
    }

}

It can be used as: Rest client


The feature of defining all bindings on interface actually got implement recently in Spring 5.1.5.

Please see this issue: https://github.com/spring-projects/spring-framework/issues/15682 - it was a struggle :)

Now you can actually do:

@RequestMapping("/random")
public interface RandomDataController {

    @RequestMapping(value = "/{type}", method = RequestMethod.GET)
    @ResponseBody
    RandomData getRandomData(
            @PathVariable(value = "type") RandomDataType type, @RequestParam(value = "size", required = false, defaultValue = "10") int size);
}
@Controller
public class RandomDataImpl implements RandomDataController {

    @Autowired
    private RandomGenerator randomGenerator;

    @Override
    public RandomData getPathParamRandomData(RandomDataType type, int size) {
        return randomGenerator.generateRandomData(type, size);
    }
}

You can even use this library: https://github.com/ggeorgovassilis/spring-rest-invoker

To get a client-proxy based on that interface, similarly to how RestEasys client framework works in the JAX-RS land.