yohjizzz's Blog

I'm a Programmer.

Spring 2.5 で Web 開発(1)Controller編 〜アノテーションでURLマッピング〜

まずはソースコード。簡単なログイン機能を実装してみました。

x.web.LoginController

package x.web;

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;
import x.dto.LoginDto; // DTO は次のエントリーで
import x.service.LoginService; // Service は次の次のエントリーで

@Controller("/login.html") // ここがポイント! URLをこのクラスと紐づけます○
public class LoginController { // POJOでokだよ○

    @Resource // JSR-250を利用すると setter がなくてもバインドしてくれる○
    private Validator validator;

    @Resource // name属性を指定してJNDIを参照することも可能だよ○
    private LoginService loginService;

    @SuppressWarnings("unchecked")
    @RequestMapping(method = RequestMethod.GET) // HTTP GETならこのメソッドがコールされる○
    public String view(ModelMap modelMap) {
        modelMap.put("login", new LoginDto());
        return "login";
    }

    @RequestMapping(
        method = RequestMethod.POST, // HTTP POST かつ、、
        params = {"doLogin"}) // パラメータに doLogin があればコールされる○
    public String doLogin(
        @ModelAttribute("login") LoginDto login, // form を DTO にバインド○
        BindingResult result,
        SessionStatus status) {
        this.validator.validate(login, result);
        if (result.getAllErrors() != null && 0 < result.getAllErrors().size()) {
            return "login";
        }
        if (this.loginService.login(login.getEmail(), login.getPassword())) {
            status.setComplete();
            return "redirect:index.html"; // リダイレクトは redirect: でok○
        } else {
            return "login";
        }
    }

    @RequestMapping(
        method = RequestMethod.POST, // HTTP POST かつ、、
        params = {"doCancel"}) // パラメータに doCancel があればコールされる○
    public String doCancel(
        @ModelAttribute("login") LoginDto login, // form を DTO にバインド○
        BindingResult result,
        SessionStatus status) {
        if (login != null) {
            login.setEmail(null);
            login.setPassword(null);
        }
        return "login";
    }

}

続いて XML です。2.5から導入された component:scan を利用して自動登録させるようにします。

applicationContext.xml または {servlet.name}-servlet.xml

  <context:component-scan
    base-package="x.web"
    scoped-proxy="targetClass"
    use-default-filters="false">
    <context:include-filter
      type="annotation"
      expression="org.springframework.stereotype.Controller" />
  </context:component-scan>
  • context:component-scan#base-package:

 →サブパッケージ含めコンポーネントを検出してくれる。

  • context:component-scan#scoped-proxy:

 →AOPプロキシ。詳細は別途。CGLIB使うなら"targetClass"。デフォルトは"interfaces"。

  • context:component-scan#use-default-filters:

 →trueだとパッケージ配下すべてを対象とする。

  • context:include-filter:

 →対象とするクラスを限定する。上記の場合 @Controller アノテーションが指定されているやつだけ○


URLとのマッピングはコントローラ(@Controller)毎じゃなくてメソッド(@RequestMapping)毎もできるけど、
個人的には≪1コンポーネント=1ページ(URL)≫ですっきりさせたい。
あと遷移も(基本的には)リダイレクト使ってPRG(Post Redirect GET)を採用したいので、
redirect:xxx.html じゃなくてリダイレクト向けの InternalResourceViewResolver みたいなのがあって、
プレフィックスとサフィックスを指定できると良いかも。
入力チェックエラーなんかでページ遷移が発生しないような場合はディスパッチでも良いので、
簡単に切り分けられると良いね。


所感としてはこんなとこかな。Url Rewrite Filter と連携して RESTful ってのもおもしろい。