自作フレームワーク
このフレームワークは私が学生時代に作ったWebフレームワークに少し手を入れようと思っているものです
元々は学校の課題としてチーム開発を行った際、デフォルトのサーブレット実装では記述が冗長になりすぎるため、より簡潔に記述するためのフレームワークが欲しい、ということで実装したものでした
概観
ざっとしたクラス図は以下のようなものです
敢えて省略しているものや記述漏れ等あるかもしれません
特徴
サーブレットの延長線上を意識したAPI
私のいたチームでは個々の経験がばらばらだったため、全員がある程度触った経験があるサーブレットからの乖離を少なくしよう、という方針を立てていました
記述量を極力減らす
特にリクエストパラメータの扱いやレスポンスの扱いについて言えることですが、サーブレットAPIではただただ煩雑で冗長な記述が多いです。ここをいかに簡略化するかが大きな課題でした
サンプルコード
リクエストパラメータの処理
まずは普通のサーブレットコード
サーブレットのエクササイズでよくあるコードとして、以下のようなものがありがちだと思います
Book newBook = new Book();
newBook.setTitle(request.getParameter("title"));
newBook.setAuthor(request.getParameter("author"));
newBook.setPrice(Integer.valueOf(request.getParameter("price")));
newBook.setPublisher(request.getParameter("publisher"));
// 以下ひたすら退屈で冗長なコード
これを書いてる時間がどうしようもなく無駄に思えますよね
私たちの出した一つの答え
これを改善すべく案を出し合った結果、採用されたのは以下のような機能でした
// このrequestはRequestというHttpServletRequestをラップする自作クラス
Book newBook = request.getParamsTo(Book.class);
察しの良い方は想像がついたかと思いますが、JavaのリフレクションAPIを使い、パラメータ名と一致する名前を持つフィールドに、その値を紐づけています
しかしHTML上のフィールド名とJavaのフィールド名が必ずしも一致させられない場合があるので、以下のような機能も用意しました
@Param(name = "book_name")
private String name;
アノテーションでHTML上のフィールド名を明示することで、名前が違っても紐づけられるようにしているわけです
また、逆に同一名のフィールドが存在するが、値をセットしたくない場合は
@Ignore
private Integer id;
のようにして無視させることもできます
レスポンスの処理
レスポンスの処理も全て自前で実装するとかなり面倒な処理だと思います
サーブレットの場合
RequestDispatcher dispacher = request.getRequestDispatcher(url);
dispatcher.forward(request, response);
さらにこれに加えてtry~catch文を強制されるので、地味に煩雑な記述になりがちです
解決策
return new Forward(url);
非常に簡潔になったと思います
このフレームワークではコントローラ内でレスポンスの処理記述を書くのではなく、レスポンスを処理する責務を負ったResponseインタフェースの実装がレスポンスを処理します
コントローラはそのResponseのインスタンスを返すだけで、後の処理はフレームワークから呼ばれるResponseが行ってくれます
(当時のプロジェクトではクラス図に示したいくつかの組み込み実装だけで十分間に合ったため、アプリケーション側でResponseを新たに実装する必要はありませんでした)
また、エラーハンドリングはフレームワークが行うためtry~catchを強制されることもなくなりました
結びに
ルーティングや設定ファイルの記述については大して面白い話もないので省略しました
ソースコードについては手元にあるものの状態が古かったり整合性が取れてないため、追々手直ししつつGitHubなりで公開していけたらと思います
おまけ:検索機能の実装例
ビューから本の検索条件としてフィールドの値を受け取り、その値を持った本を検索してJSON形式で返す、という機能について
public BookController {
public Response search(Request request) {
// まずはビューから送られてきた検索パラメータをBookに投入
Book searchCondition = request.getParamsTo(Book.class);
// 次に、同じ値を持つBookのリストを取得
List<Book> books = new FasterDAO().find(searchCondition);
// 検索結果をJSON形式で返す
return new Json(books);
}
}
FasterDAOはクラス図に書いてありませんが、JPAをラップしてより簡単に使えるようにしたクラスです
このように冗長でありながら実際には値しか変わらないような処理をとことん汎用的なクラス、メソッド化することで作業の効率化を狙いました