Spring-MVC Проблема с использованием @Controller на контроллере, реализующем интерфейс - PullRequest
33 голосов
/ 30 сентября 2008

Я использую 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 должна быть достаточной, чтобы пометить это как контроллер, и я должен иметь возможность реализовать один или несколько интерфейсов в моем контроллере, не делая ничего другого. Есть идеи?

Ответы [ 6 ]

12 голосов
/ 07 июля 2010

Что мне нужно было сделать, это заменить

 <tx:annotation-driven/>

с

 <tx:annotation-driven  proxy-target-class="true"/>

Это заставляет aspectj использовать CGLIB для выполнения аспектов вместо динамических прокси - CGLIB не теряет аннотацию, поскольку расширяет класс, тогда как динамические прокси просто предоставляют реализованный интерфейс.

12 голосов
/ 01 марта 2010

Эд прав, добавив

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

отлично работает

10 голосов
/ 23 октября 2012

Если вы хотите использовать интерфейсы для своих контроллеров Spring MVC, вам нужно немного переместить аннотации, как указано в документации Spring: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping

Использование @RequestMapping для методов интерфейса Распространенная ошибка, когда работа с аннотированными классами контроллеров происходит при применении функциональность, которая требует создания прокси для объекта контроллера (например, @ Транзакционные методы). Обычно вы будете вводить интерфейс для контроллера, чтобы использовать динамические прокси 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, например, в конкретный класс. Это только аннотации типа @Controller, которые вам нужно поместить в интерфейс из-за того, как Spring выполняет свои отображения.

Обратите внимание, что вам нужно делать это только при использовании интерфейса. Вам не обязательно делать это, если вы довольны прокси-серверами CGLib, но если по какой-то причине вы хотите использовать динамические прокси-серверы JDK, это может быть путь.

5 голосов
/ 10 декабря 2008

Нет сомнений в том, что аннотации и наследование могут быть немного сложными, но я думаю, что это должно сработать. Попробуйте явно добавить AnnotationMethodHandlerAdapter в свой контекст сервлета.

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

Если это не сработает, будет полезно немного больше информации. В частности, являются ли два аннотированных метода контроллера из интерфейса? Foo должен быть RegistrationController?

3 голосов
/ 13 апреля 2016

Я знаю, что уже слишком поздно, но я пишу это для всех, у кого есть эта проблема если вы используете конфигурацию на основе аннотаций ... решение может быть таким:

@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
0 голосов
/ 05 сентября 2010

Истинная причина, по которой вам нужно использовать 'proxy-target-class = "true"', заключается в методе DefaultAnnotationHandlerMapping#determineUrlsForHandler(): хотя он использует ListableBeanFactory#findAnnotationOnBean для поиска аннотации @RequestMapping (и это касается любого прокси-сервера) проблем), дополнительный поиск для аннотации @Controller выполняется с использованием AnnotationUtils#findAnnotation (который не обрабатывает проблемы с прокси)

...