Подтвердить что ты не робот

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

Я использую 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() {
  ...
  }
}

работает отлично. Когда контекст запускается, ссылки, обработанные этим обработчиком, обнаруживаются, и все работает отлично.

Однако это не так:

@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 [[email protected]]: 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 должны быть достаточными, чтобы отметить это как контроллер, и я должен иметь возможность реализовать один или несколько интерфейсов в моем контроллере, не делая ничего другого. Любые идеи?

4b9b3361

Ответ 1

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

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

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

Ответ 2

Мне нужно было заменить

 <tx:annotation-driven/>

с

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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