web-dev-qa-db-ja.com

インターフェースを実装するコントローラーで@Controllerを使用するSpring-MVCの問題

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() {
  ...
  }
}

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アノテーションは、これをコントローラーとしてマークするのに十分である必要があり、他に何もすることなく、コントローラーに1つ以上のインターフェースを実装できるはずです。何か案は?

34
layne

私がする必要があったのは交換です

 <tx:annotation-driven/>

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

これにより、aspectjは動的プロキシの代わりにCGLIBを使用してアスペクトを実行します-CGLIBはクラスを拡張するため注釈を失うことはありませんが、動的プロキシは実装されたインターフェースを公開するだけです。

13
James Kingsbery

Edは正しい、追加

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

正常に動作します

12
Michal Bachman

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"をアクティブにすることもできます(のトランザクションシナリオ)。そうすることは、インターフェースベースのJDKプロキシの代わりに、CGLIBベースのサブクラスプロキシを使用する必要があることを示します。さまざまなプロキシメカニズムの詳細については、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動的プロキシを使用したい場合は、これが適切な方法かもしれません。

10
Kieran

アノテーションと継承が少しトリッキーになることは間違いありませんが、私はそれでうまくいくと思います。 AnnotationMethodHandlerAdapterをサーブレットコンテキストに明示的に追加してみてください。

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

それでもうまくいかない場合は、もう少し情報が参考になります。具体的には、インターフェースからの2つの注釈付きコントローラーメソッドですか? FooはRegistrationControllerであるはずですか?

5
Ed Thomas

手遅れであることがわかっていますが、注釈ベースの構成を使用している場合は、この問題を抱えている人のためにこれを書いています...解決策は次のようになります:

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

「proxy-target-class = "true"」を使用する必要がある本当の理由は、DefaultAnnotationHandlerMapping#determineUrlsForHandler()メソッドにあります。ただし、ListableBeanFactory#findAnnotationOnBeanを使用して@RequestMappingアノテーションを検索します(これプロキシの問題に注意)、@Controllerアノテーションの追加ルックアップはAnnotationUtils#findAnnotationを使用して行われます(プロキシの問題を処理しません)

0
Boris Kirzner