Я использую Spring 2.5 и аннотации для настройки моего веб-контекста spring-mvc. К сожалению, я не могу заставить работать следующее. Я не уверен, является ли это ошибкой (кажется, так) или существует базовое недоразумение относительно того, как работают подклассы аннотаций и реализации интерфейса.
Например,
@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() {
...
}
}
Когда я пытаюсь открыть 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)
Однако, если я сделаю 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 должно быть достаточно, чтобы пометить его как контроллер, и я должен иметь возможность реализовать один или несколько интерфейсов в своем контроллере без необходимости делать что-либо еще. Есть идеи?




Нет сомнений в том, что аннотации и наследование могут быть немного сложными, но я думаю, что это должно сработать. Попробуйте явно добавить AnnotationMethodHandlerAdapter в контекст сервлета.
http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup
Если это не сработает, будет полезно немного дополнительной информации. В частности, являются ли два аннотированных метода контроллера из интерфейса? Должен ли Foo быть RegistrationController?
Эд прав, добавляя
<bean class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
работает отлично
Мне нужно было заменить
<tx:annotation-driven/>
с
<tx:annotation-driven proxy-target-class = "true"/>
Это заставляет аспектj использовать CGLIB для выполнения аспектов вместо динамических прокси - CGLIB не теряет аннотацию, поскольку он расширяет класс, тогда как динамические прокси просто открывают реализованный интерфейс.
Истинная причина, по которой вам нужно использовать 'proxy-target-class = "true"', заключается в методе DefaultAnnotationHandlerMapping#determineUrlsForHandler(): хотя он использует ListableBeanFactory#findAnnotationOnBean для поиска аннотации @RequestMapping (и это решает любые проблемы с прокси), дополнительный поиск аннотации @Controller выполняется с использованием AnnotationUtils#findAnnotation (который не обрабатывает проблемы с прокси)
Вы имеете в виду ошибку?
Если вы хотите использовать интерфейсы для своих контроллеров 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”.
К сожалению, здесь нет конкретного примера. Я обнаружил, что работает такая установка:
@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, например, в конкретный класс. В интерфейсе необходимо добавить только аннотации типа @Controller, поскольку Spring выполняет свои сопоставления.
Обратите внимание, что вам нужно сделать это только в том случае, если вы используете интерфейс. Вам не обязательно делать это, если вам нравятся прокси CGLib, но если по какой-то причине вы хотите использовать динамические прокси JDK, это может быть подходящим вариантом.
Я знаю, что уже слишком поздно, но я пишу это для тех, у кого есть эта проблема если вы используете конфигурацию на основе аннотаций ... решение может быть таким:
@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
Необходимо отметить, что CGLIB скорее устарел.