2

Spring MVC でバージョン管理されたサービスをセットアップしようとしています。継承を使用して古いコントローラーを拡張し、変更されていないコントローラー メソッドを書き換えないようにしています。バージョン管理サービスに関する以前の質問に基づいてソリューションを作成しましたが、あいまいなマッピングの問題に遭遇しました。

@Controller
@RequestMapping({"/rest/v1/bookmark"})
public class BookmarkJsonController {

  @ResponseBody
  @RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
  public Map<String, String> writeBookmark(@RequestParam String parameter) {
    // Perform some operations and return String
  }
}

@Controller
@RequestMapping({"/rest/v2/bookmark"})
public class BookmarkJsonControllerV2 extends BookmarkJsonController {

  @ResponseBody
  @RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
  public BookmarkJsonModel writeBookmark(@RequestBody @Valid BookmarkJsonModel bookmark) {
    // Perform some operations and return BookmarkJsonModel
  }
}

このセットアップで私は得IllegalStateException: Ambiguous mapping foundます。BookmarkJsonControllerV2これに関する私の考えは、戻り値/引数の型が異なる 2 つのメソッドがあるため、同じマッピングを持つ 2 つのメソッドがあるということです。回避策として、リクエストマッピングなしでオーバーライドしようとしwriteBookmarkました:BookmarkJsonControllerV2

@Override
public Map<String, String> writeBookmark(@RequestParam String parameter) {
  return null; // Shouldn't actually be used
}

ただし、このコードをコンパイルして実行すると、まだあいまいなマッピングの例外が発生します。ただし、URL/rest/v2/bookmark/writeにアクセスすると、空/null の応答が返されました。変更後return null:

return new HashMap<String, String>() {{
  put("This is called from /rest/v2/bookmark/write", "?!");
}};

そのマップで JSON を受け取ります。これは、リクエスト マッピング アノテーションがないにもかかわらず、明らかにスーパー クラスからアノテーションを「継承」していることを示しています。この時点で、コントローラーの拡張を将来的に保証するための唯一の「解決策」は、すべてのコントローラーを返して、オブジェクトとオブジェクトObjectのみを引数として持つことです。これは完全なハックのように思えます。HttpServletRequestHttpServletResponse

では、Spring MVC を使用して URL のバージョン管理を実現するためのより良い方法はありますか?これにより、後続のバージョンで更新されたメソッドのみをオーバーライドできるようになりますか、それとも各コントローラーを完全に書き換える唯一の現実的なオプションですか?

4

1 に答える 1

3

何らかの理由で、@RequestMapping注釈を使用すると、あいまいなマッピング例外が発生していました。回避策として、REST サービスにspringmvc-routerを使用してみることにしました。これにより、コントローラー クラスで継承を利用できるようになり、バージョン間で変更されなかったエンドポイントを必要に応じて再実装する必要がなくなります。私のソリューションでは、REST 以外のコントローラーにアノテーション マッピングを引き続き使用することもできました。

: Spring 3.1 を使用しています。Spring 3.1 は、以前のバージョンとはハンドラー マッピングのクラスが異なります。

springmvc-router プロジェクトは、ルーター システムを Play フレームワークから Spring MVC にもたらします。my の内部ではapplication-context.xml、関連するセットアップは次のようになります。

<mvc:annotation-driven/>
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />

<bean class="org.resthub.web.springmvc.router.RouterHandlerMapping">
    <property name="routeFiles">
        <list>
            <value>routes/routes.conf</value>
        </list>
    </property>
    <property name="order" value="0" />
</bean>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="order" value="1" />
</bean>

これにより、注釈付きコントローラーをルーターと一緒に使用し続けることができます。Spring は一連の責任システムを使用するため、複数のマッピング ハンドラーを割り当てることができます。ここから、次のようなルーター構成があります。

# Original Services

POST    /rest/bookmark/write     bookmarkJsonController.write
POST    /rest/bookmark/delete    bookmarkJsonController.delete

# Version 2 Services

POST    /rest/v2/bookmark/write  bookmarkJsonControllerV2.write
POST    /rest/v2/bookmark/delete bookmarkJsonControllerV2.delete

次のようなコントローラーと並んで:

@Controller
public class BookmarkJsonController {
  @ResponseBody
  public Map<String, Boolean> write(@RequestParam String param) { /* Actions go here */ }

  @ResponseBody
  public Map<String, Boolean> delete(@RequestParam String param) { /* Actions go here */ }
}

@Controller
public class BookmarkJsonControllerV2 extends BoomarkJsonController {
  @ResponseBody
  public Model write(@RequestBody Model model) { /* Actions go here */ }
}

このような構成であれば、/rest/v2/bookmark/writeメソッドBookmarkJsonControllerV2.write(Model model)に URL/rest/v2/bookmark/deleteがヒットし、継承されたメソッドに URL がヒットしますBookmarkJsonController.delete(String param)

これによる唯一の欠点は、クラスの@RequestMapping(value = "/rest/bookmark")toを変更するのではなく、新しいバージョンのルート全体を再定義する必要があることです。@RequestMapping(value = "/rest/v2/bookmark")

于 2013-08-01T11:46:24.220 に答える