Spring-MVC在控制器实现接口时使用@Controller的问题

lay*_*yne 34 java annotations controller spring-mvc

我正在使用spring 2.5和注释来配置我的spring-mvc web上下文.不幸的是,我无法让以下工作.我不确定这是一个错误(似乎是这样),或者是否存在对注释和接口实现子类化如何工作的基本误解.

例如,

@Controller
@RequestMapping("url-mapping-here")
public class Foo {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}
Run Code Online (Sandbox Code Playgroud)

工作良好.当上下文启动时,会发现此处理程序处理的URL,并且一切都很好.

然而,这不是:

@Controller
@RequestMapping("url-mapping-here")
public class Foo implements Bar {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试拉出网址时,我得到以下令人讨厌的堆栈跟踪:

javax.servlet.ServletException: No adapter for handler [com.shaneleopard.web.controller.RegistrationController@e973e3]: Does your handler implement a supported interface like Controller?
    org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1091)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:874)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
Run Code Online (Sandbox Code Playgroud)

但是,如果我将Bar更改为抽象超类并让Foo扩展它,那么它再次起作用.

@Controller
@RequestMapping("url-mapping-here")
public class Foo extends Bar {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这似乎是一个错误.@Controller注释应足以将其标记为控制器,并且我应该能够在控制器中实现一个或多个接口,而无需执行任何其他操作.有任何想法吗?

Jam*_*ery 13

我需要做的就是更换

 <tx:annotation-driven/>
Run Code Online (Sandbox Code Playgroud)

 <tx:annotation-driven  proxy-target-class="true"/>
Run Code Online (Sandbox Code Playgroud)

这迫使aspectj使用CGLIB来做方面而不是动态代理 - CGLIB不会丢失注释,因为它扩展了类,而动态代理只是公开实现的接口.


Mic*_*man 12

艾德是对的,补充道

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
Run Code Online (Sandbox Code Playgroud)

工作良好


Kie*_*ran 10

如果您希望为Spring MVC控制器使用接口,那么您需要稍微移动注释,如Spring文档中所述:http://static.springsource.org/spring/docs/3.1.x/spring-framework -reference/HTML/mvc.html#MVC-ANN-requestmapping

在接口方法上使用@RequestMapping在使用需要为控制器对象创建代理的功能时(例如@Transactional方法),在使用带注释的控制器类时会发生常见的陷阱.通常,您将为控制器引入一个接口,以便使用JDK动态代理.要使这项工作,您必须将@RequestMapping注释移动到接口,并且映射机制只能"看到"代理公开的接口.或者,您可以在配置中激活proxy-target-class ="true"以应用于控制器的功能(在我们的事务方案中).这样做表明应该使用基于CGLIB的子类代理而不是基于接口的JDK代理.

不幸的是,它没有给出一个具体的例子.我发现这样的设置有效:

@Controller
@RequestMapping(value = "/secure/exhibitor")
public interface ExhibitorController {

    @RequestMapping(value = "/{id}")
    void exhibitor(@PathVariable("id") Long id);
}

@Controller
public class ExhibitorControllerImpl implements ExhibitorController {

    @Secured({"ROLE_EXHIBITOR"})
    @Transactional(readOnly = true)
    @Override
    public void exhibitor(final Long id) {

    }
}
Run Code Online (Sandbox Code Playgroud)

所以你所拥有的是一个声明@ Controller,@ PathVariable和@RequestMapping注释(Spring MVC注释)的接口,然后你可以将@Transactional或@Secured注释放在具体类上.由于Spring的映射方式,只需要在接口上放置@Controller类型注释.

请注意,如果使用接口,则只需执行此操作.如果您对CGLib代理感到满意,则不一定需要这样做,但如果由于某种原因您想要使用JDK动态代理,那么这可能就是您的选择.


Ed *_*mas 5

毫无疑问,注释和继承可能会有点棘手,但我认为这应该有效.尝试将AnnotationMethodHandlerAdapter显式添加到servlet上下文中.

http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup

如果这不起作用,更多信息会有所帮助.具体来说,界面中有两个带注释的控制器方法吗?Foo应该是RegistrationController吗?


Ami*_*mir 5

我知道为时已晚,但如果您使用基于注释的配置,我正在为任何遇到此问题的人编写此内容...解决方案可能如下所示:

@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
Run Code Online (Sandbox Code Playgroud)