【问题标题】:Inheritance in Spring RestControllerSpring RestController 中的继承
【发布时间】:2016-01-04 22:20:38
【问题描述】:

我有一个处理 API 调用版本 1 的 Spring RestController。

package rest.v1;

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

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

我想做的是创建另一个处理版本 2 请求的控制器。同时我想继承已经在版本 1 控制器中的实现。这是因为从 v1 到 v2 未更改的实现将只是从 v1 继承。

所以是这样的:

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
    }
}

这样做会导致 ConflictingBeanDefinitionException。我也不确定是否应该像上面那样扩展控制器。

我知道我可以像这样在同一个控制器的 RequestMapping 中使用多个路径:

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

}

但以上内容不适用于我的场景。

这是扩展控制器时的堆栈跟踪:

 [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)

【问题讨论】:

  • 请向我们展示您的 XML 配置。

标签: java spring rest


【解决方案1】:

@RestController 扩展当前可用。由于新的方法实现版本,我需要扩展 @RestController。我试图扩展一个@RestController,它可以完美地工作而无需指定spring bean的名称。这是一个简单的例子:

@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 .....
    }
    
}

在这种情况下,您可以通过两种不同的方式调用“mymethod1”和“mymethod2”,分别是

http://hostname/v1/mycontroller/mymethod1

http://hostname/v2/mycontroller/mymethod1

http://hostname/v1/mycontroller/mymethod2

http://hostname/v2/mycontroller/mymethod2

“mymethod1”有两种不同的实现,“mymethod2”只有一种实现,即“MyControllerV1”继承自“MyControllerV2”

【讨论】:

    【解决方案2】:

    您不仅不能以这种方式共享映射,而且继承似乎根本不适用于@RestController。这样做会导致您的控制器默默地无法获取您的任何 @RequestMapping 注释方法。弄清楚这一点需要相当长的时间,但您可以检查日志文件的输出以获取它不工作的提示。具体来说,如果您没有看到在AbstractHandlerMethodMapping 中为您的@RestController 打印的以下日志行,您将知道您的方法处理不起作用:

        if (logger.isDebugEnabled()) {
            logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
        }
    

    【讨论】:

      【解决方案3】:

      stacktrace 的第一行很清楚问题:

      org.springframework.beans.factory.BeanDefinitionStoreException:从类路径资源 [rest-context.xml] 解析 XML 文档时出现意外异常;嵌套异常是 org.springframework.context.annotation.ConflictingBeanDefinitionException:注解指定的 bean name 'controller_V1' for bean class [rest.v2.Controller_V2] 与现有的、不兼容的同名 bean 定义和类 [rest.v1.Controller_V1]

      这是因为您让 Spring 为您的 bean 命名。默认情况下,Spring 会使用 camelCase 中当前类的接口或超类的名称作为 bean 名称。

      所以,你有这样的东西(Spring 给出的名称):

      @RestController("controller_V1") //<-- that's the name of the Spring bean
      @RequestMapping("v1/someResource")
      public class Controller_V1
      
      @RestController("controller_V1") //<-- that's the name of the Spring bean
      @RequestMapping("v2/someResource")
      public class Controller_V2 extends Controller_V1
      

      如您所见,您只需要重命名您的第二个 bean:

      @RestController("controller_V2")
      @RequestMapping("v2/someResource")
      public class Controller_V2 extends Controller_V1
      

      IMO 你应该为你的类使用更好的名字。

      【讨论】:

      • 感谢您的建议。我应该提到我已经尝试分配第二个 bean 的名称,这样就不会有命名冲突。它仍然给出同样的错误。在此基础上还编辑了问题。
      • @SJha 错误消息是明确的。尝试此操作后获得相同消息的唯一方法意味着您在其他地方声明了 bean 并犯了同样的错误。请显示 bean 配置。
      猜你喜欢
      • 2019-08-03
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 2019-03-27
      • 2019-07-16
      • 2011-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多