Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance in Spring RestController

Tags:

java

rest

spring

I have a Spring RestController which handles version 1 of API calls.

package rest.v1;

@RestController
@RequestMapping("v1/someResource")
public class Controller_V1 {

    @RequestMapping(value = "/{resourceName}", method = RequestMethod.GET)
    public Object retrieve() throws Exception {
        ....
    }
}

What I want to do is create another controller which handles version 2 requests. At the same time I want to inherit the implementation which are already in version 1 controller. This is because the implementations which are unchanged from v1 to v2 will just be inherited as it is from v1.

So something like this:

package rest.v2;

@RestController("controllerV2")
@RequestMapping("v2/someResource")
public class Controller_v2 extends Controller_v1 {

    @RequestMapping(value = "/{resourceName}", method = RequestMethod.GET)
    public Object retrieve() throws Exception {
        //implementation overridden from v1
    }
}

Doing it results in ConflictingBeanDefinitionException. I am also not sure if extending the controllers like above is supposed to work.

I am aware that I can use multiple paths in RequestMapping of the same controller like this:

@RestController
@RequestMapping(value = { "v1/someResource", "v1/someResource" })
public class Controller_V1 {

}

But the above is not going to work for my scenario.

Here is the stack trace when the controllers are extended:

 [java] org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [rest-context.xml]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'controller_V1' for bean class [rest.v2.Controller_V2] conflicts with existing, non-compatible bean definition of same name and class [rest.v1.Controller_V1]
     [java]     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:413)
     [java]     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335)
     [java]     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303)
     [java]     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
     [java]     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)
     [java]     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
     [java]     at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
     [java]     at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
     [java]     at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
     [java]     at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:540)
     [java]     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:454)
     [java]     at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
     [java]     at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624)
     [java]     at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672)
     [java]     at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543)
     [java]     at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
     [java]     at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
     [java]     at javax.servlet.GenericServlet.init(GenericServlet.java:158)
like image 777
SJha Avatar asked Oct 07 '15 15:10

SJha


People also ask

What does @RestController do in Spring?

Spring RestController annotation is used to create RESTful web services using Spring MVC. Spring RestController takes care of mapping request data to the defined request handler method. Once response body is generated from the handler method, it converts it to JSON or XML response.

What is the difference between @component and @RestController?

It is a specialized version of @Controller annotation. In @Controller, we can return a view in Spring Web MVC. In @RestController, we can not return a view. @Controller annotation indicates that the class is a “controller” like a web controller.

Is @requestbody required with @RestController?

Remember, we don't need to annotate the @RestController-annotated controllers with the @ResponseBody annotation since Spring does it by default.

Can we replace @controller with @RestController?

So if you are using latest spring version you can directly replace @Controller with @RestController without any issue.


1 Answers

A @RestController extension is currently available. I needed to extend a @RestController due to a new method implementation version. I tried to extend a @RestController and it works perfectly without specifying the name of the spring bean. Here is a simple example:

@RestController
@RequestMapping("/v1/mycontroller")
public class MyControllerV1 {
    
    @RequestMapping("/mymethod1")
    public Object mymethod1(@RequestBody Object myRequest) {
        <my v1 implementation>
        return .....
    }
    
    @RequestMapping("/mymethod2")
    public Object mymethod2(@RequestBody Object myRequest) {
        <my v1 implementation>
        return .....
    }
}



@RestController
@RequestMapping("/v2/mycontroller")
public class MyControllerV2 extends MyControllerV1 {
    
    @RequestMapping("/mymethod1")
    public Object mymethod1(@RequestBody Object myRequest) {
        <my v2 implementation>
        return .....
    }
    
}

In this case you can call "mymethod1" and "mymethod2" in two different ways, which are

http://hostname/v1/mycontroller/mymethod1 <-- first implementation -->

http://hostname/v2/mycontroller/mymethod1 <-- second implementation -->

http://hostname/v1/mycontroller/mymethod2 <-- same implementation -->

http://hostname/v2/mycontroller/mymethod2 <-- same implementation -->

"mymethod1" has two different implementation and "mymethod2" has only one implemation that "MyControllerV1" has inherited from "MyControllerV2"

like image 124
walthor Avatar answered Sep 18 '22 18:09

walthor