Java 在实现接口的控制器上使用 @Controller 的 Spring-MVC 问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/154042/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring-MVC Problem using @Controller on controller implementing an interface
提问by layne
I'm using spring 2.5 and annotations to configure my spring-mvc web context. Unfortunately, I am unable to get the following to work. I'm not sure if this is a bug (seems like it) or if there is a basic misunderstanding on how the annotations and interface implementation subclassing works.
我正在使用 spring 2.5 和注释来配置我的 spring-mvc web 上下文。不幸的是,我无法使以下内容起作用。我不确定这是否是一个错误(似乎是这样),或者是否对注释和接口实现子类化的工作方式存在基本误解。
For example,
例如,
@Controller
@RequestMapping("url-mapping-here")
public class Foo {
@RequestMapping(method=RequestMethod.GET)
public void showForm() {
...
}
@RequestMapping(method=RequestMethod.POST)
public String processForm() {
...
}
}
works fine. When the context starts up, the urls this handler deals with are discovered, and everything works great.
工作正常。当上下文启动时,会发现此处理程序处理的 url,并且一切正常。
This however does not:
然而,这不会:
@Controller
@RequestMapping("url-mapping-here")
public class Foo implements Bar {
@RequestMapping(method=RequestMethod.GET)
public void showForm() {
...
}
@RequestMapping(method=RequestMethod.POST)
public String processForm() {
...
}
}
When I try to pull up the url, I get the following nasty stack trace:
当我尝试拉起 url 时,我得到以下令人讨厌的堆栈跟踪:
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)
However, if I change Bar to be an abstract superclass and have Foo extend it, then it works again.
但是,如果我将 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() {
...
}
}
This seems like a bug. The @Controller annotation should be sufficient to mark this as a controller, and I should be able to implement one or more interfaces in my controller without having to do anything else. Any ideas?
这似乎是一个错误。@Controller 注释应该足以将其标记为控制器,并且我应该能够在我的控制器中实现一个或多个接口而无需执行任何其他操作。有任何想法吗?
回答by Ed Thomas
There's no doubt that annotations and inheritance can get a little tricky, but I think that should work. Try explicitly adding the AnnotationMethodHandlerAdapter to your servlet context.
毫无疑问,注释和继承会变得有点棘手,但我认为这应该可行。尝试将 AnnotationMethodHandlerAdapter 显式添加到您的 servlet 上下文。
http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup
http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup
If that doesn't work, a little more information would be helpful. Specifically, are the two annotated controller methods from the interface? Is Foo supposed to be RegistrationController?
如果这不起作用,多一点信息会有所帮助。具体来说,两个带注释的控制器方法是否来自接口?Foo 应该是 RegistrationController 吗?
回答by Michal Bachman
Ed is right, adding
埃德是对的,补充说
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
works fine
工作正常
回答by James Kingsbery
What I needed to do was replace
我需要做的是更换
<tx:annotation-driven/>
with
和
<tx:annotation-driven proxy-target-class="true"/>
This forces aspectj to use CGLIB for doing aspects instead of dynamic proxies - CGLIB doesn't lose the annotation since it extends the class, whereas dynamic proxies just expose the implemented interface.
这迫使aspectj 使用CGLIB 来做aspect 而不是动态代理——CGLIB 不会丢失注释,因为它扩展了类,而动态代理只是公开了实现的接口。
回答by Boris Kirzner
The true reason you need to use 'proxy-target-class="true"' is in DefaultAnnotationHandlerMapping#determineUrlsForHandler()
method: though it uses ListableBeanFactory#findAnnotationOnBean
for looking up a @RequestMapping
annotation (and this takes care about any proxy issues), the additional lookup for @Controller
annotation is done using AnnotationUtils#findAnnotation
(which does not handles proxy issues)
您需要使用 'proxy-target-class="true"' 的真正原因在于DefaultAnnotationHandlerMapping#determineUrlsForHandler()
方法:尽管它ListableBeanFactory#findAnnotationOnBean
用于查找@RequestMapping
注释(这会处理任何代理问题),但@Controller
使用AnnotationUtils#findAnnotation
(不处理代理问题)
回答by Kieran
If you wish to use interfaces for your Spring MVC controllers then you need to move the annotations around a bit, as mentioned in the Spring docs: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping
如果您希望为 Spring MVC 控制器使用接口,那么您需要稍微移动注释,如 Spring 文档中所述:http: //static.springsource.org/spring/docs/3.1.x/spring-framework -reference/html/mvc.html#mvc-ann-requestmapping
Using @RequestMapping On Interface Methods A common pitfall when working with annotated controller classes happens when applying functionality that requires creating a proxy for the controller object (e.g. @Transactional methods). Usually you will introduce an interface for the controller in order to use JDK dynamic proxies. To make this work you must move the @RequestMapping annotations to the interface as well as the mapping mechanism can only "see" the interface exposed by the proxy. Alternatively, you could activate proxy-target-class="true" in the configuration for the functionality applied to the controller (in our transaction scenario in ). Doing so indicates that CGLIB-based subclass proxies should be used instead of interface-based JDK proxies. For more information on various proxying mechanisms see Section 8.6, “Proxying mechanisms”.
在接口方法上使用@RequestMapping 在应用需要为控制器对象创建代理的功能(例如@Transactional 方法)时,使用带注释的控制器类时会发生一个常见的陷阱。通常你会为控制器引入一个接口,以便使用 JDK 动态代理。要完成这项工作,您必须将 @RequestMapping 注释移动到接口,并且映射机制只能“看到”代理公开的接口。或者,您可以在配置中为应用于控制器的功能激活 proxy-target-class="true"(在我们的事务场景中)。这样做表明应该使用基于 CGLIB 的子类代理而不是基于接口的 JDK 代理。
Unfortunately it doesn't give a concrete example of this. I have found a setup like this works:
不幸的是,它没有给出一个具体的例子。我发现这样的设置有效:
@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) {
}
}
So what you have here is an interface that declares the @Controller, @PathVariable and @RequestMapping annotations (the Spring MVC annotations) and then you can either put your @Transactional or @Secured annotations for instance on the concrete class. It is only the @Controller type annotations that you need to put on the interface because of the way Spring does its mappings.
所以你这里有一个接口,它声明了@Controller、@PathVariable 和 @RequestMapping 注释(Spring MVC 注释),然后你可以将你的 @Transactional 或 @Secured 注释放在具体类上。由于 Spring 进行映射的方式,您只需要在接口上放置 @Controller 类型的注释。
Note that you only need to do this if you use an interface. You don't necessarily need to do it if you are happy with CGLib proxies, but if for some reason you want to use JDK dynamic proxies, this might be the way to go.
请注意,只有在使用接口时才需要执行此操作。如果您对 CGLib 代理感到满意,则不一定需要这样做,但是如果出于某种原因您想使用 JDK 动态代理,这可能是要走的路。
回答by Amir
I know it is too late but i'm writing this for anyone have this problem if you are using annotation based configuration... the solution might be like this:
我知道为时已晚,但如果您使用基于注释的配置,我正在为任何有此问题的人写这篇文章......解决方案可能是这样的:
@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}