我正在使用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() {
...
}
}
工作正常。当上下文启动时,会发现此处理程序处理的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() {
...
}
}
当我尝试拉出网址时,我得到以下令人讨厌的堆栈跟踪:
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)
但是,如果我将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() {
...
}
}
这似乎是一个错误。 @Controller注释应足以将其标记为控制器,并且我应该能够在控制器中实现一个或多个接口,而无需执行任何其他操作。有什么想法吗?
答案 0 :(得分:12)
Ed是对的,添加了
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
工作正常
答案 1 :(得分:12)
我需要做的是替换
<tx:annotation-driven/>
与
<tx:annotation-driven proxy-target-class="true"/>
这迫使aspectj使用CGLIB来执行方面而不是动态代理 - CGLIB不会丢失注释,因为它扩展了类,而动态代理只是暴露了实现的接口。
答案 2 :(得分: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代理。有关各种代理的更多信息 机制见第8.6节“代理机制”。
不幸的是,它没有给出一个具体的例子。我发现这样的设置有效:
@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) {
}
}
所以你所拥有的是一个声明@ Controller,@ PathVariable和@RequestMapping注释(Spring MVC注释)的接口,然后你可以将@Transactional或@Secured注释放在具体类上。由于Spring的映射方式,只需要在接口上放置@Controller类型的注释。
请注意,如果使用界面,则只需执行此操作。如果您对CGLib代理感到满意,则不一定需要这样做,但如果由于某种原因您想使用JDK动态代理,那么这可能就是您的选择。
答案 3 :(得分:5)
毫无疑问,注释和继承可能会有点棘手,但我认为这应该有效。尝试将AnnotationMethodHandlerAdapter显式添加到servlet上下文中。
http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup
如果这不起作用,可以提供更多信息。具体来说,界面中有两个带注释的控制器方法吗? Foo应该是RegistrationController吗?
答案 4 :(得分:3)
我知道现在为时已晚,但是我为任何人写这个都有这个问题 如果你使用基于注释的配置...解决方案可能是这样的:
@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
答案 5 :(得分:0)
您需要使用'proxy-target-class =“true”'的真正原因在于DefaultAnnotationHandlerMapping#determineUrlsForHandler()
方法:尽管它使用ListableBeanFactory#findAnnotationOnBean
来查找@RequestMapping
注释(和这会照顾任何代理问题),@Controller
注释的额外查找是使用AnnotationUtils#findAnnotation
(不处理代理问题)完成的。