HandlerMappingを自動でやりたい

リクエストのパスに対応するControllerを見つけるためのクラスがHandlerMapping。
こういうのは、命名規約といったルール付けをしておくことで、実装者が
設定ファイルを書かなくても自動でできるようにしたい。


イメージとしては、/top/top.formにアクセスするとtop.TopControllerが
自動で呼ばれるようにしたい。


今考えている前提として、

という条件。


で、ControllerClassNameHandlerMappingを使うとTopControllerは
以下のURLにマッピングされると認識される。

  • top/top
  • top/top/*

つまりは、top/top.formには反応しないわけで、困った困った・・・
というのをどうにかしたいというのが今回のお話。


とりあえず思いつきで2つを試してみた。

  • ControllerClassNameHandlerMappingを拡張する
  • 独自にHandlerMappingを作る

ControllerClassNameHandlerMappingを拡張してしまう

ControllerClassNameHandlerMappingで、top.TopControllerが
/top/top*に対して登録するようにしてしまう。


コードを追う限り、ControllerClassNameHandlerMapping#generatePathMappings
でTopControllerがMultiActionControllerTypeと認識されるから悪いわけで、
そうでないと認識されれば/top/top*に対するマッピングが登録されるはず。


やるとすると以下の方法となる。

  • ControllerClassNameHandlerMappingを継承したクラスを作成し、isMultiActionControllerTypeメソッドをオーバーライドしちゃう。
  • isMultiActionControllerTypeで使っているControllerTypePredicateを置き換える。
    • →ただしこいつパッケージプライベートだったりする。


ただ、@Controllerを付けたクラスがMultiActionControllerTypeでなくなる
ことの弊害は不明・・・。

独自にHandlerMappingを作る

単にパス名とControllerをマッピングするHandlerMappingを自作してみる。
Spring全体の動作を把握しているわけじゃないので、あんまりやりたくないけど、
なんかちょっとやってみたかったというだけ。


top/top.formにアクセスした場合、top.formの部分からTopControllerが
マッピングされるようにする。テスト的に作ったのがこんな感じ。
# 時間がないのでガリガリ書いてるのはヒミツ
# 上半分はどっかのコードのパクりだしw

public class PathNameHandlerMapping extends AbstractUrlHandlerMapping {

    @Override
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        Object handler = getHandlerMap().get(urlPath);
        if (handler != null) {
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = getApplicationContext().getBean(handlerName);
            }
            validateHandler(handler, request);
            return buildPathExposingHandler(handler, urlPath, urlPath, null);
        }

        int index = urlPath.lastIndexOf('/');
        if (index == -1) {
            return null;
        }

        String path = urlPath.substring(index + 1);
        int extIndex = path.indexOf('.');
        if (extIndex != -1) {
            path = path.substring(0, extIndex);
        }

        String handlerName = path + "Controller";
        if (!getApplicationContext().containsBean(handlerName)) {
            return null;
        }

        handler = getApplicationContext().getBean(handlerName);
        validateHandler(handler, request);
        registerHandler(urlPath, handlerName);
        return buildPathExposingHandler(handler, path, path, null);
    }

}

なんとなく動いているようだけど、不正なパスに対してもControllerが動くわけで、
セキュリティ的に大問題な気がする。/aaa/xxx.formにアクセスできないユーザーが、
/xxx.formでアクセスできちゃったりしそーなので。
あと、これやるとパス名は必ず一意としなければならない(/aaa/xxx.formと
/bbb/xxx.formというのは作れない)わけで、それもよろしくない。




色々と悩んだけど、こんな解決策しか思いつかないんで、
もうちっとソースをあさってみるかぁ。
そもそも、1リクエスト=1Controllerとしてるのが悪な気がするけど。