Javaのフロントコントローラパターンの手引き

Javaのフロントコントローラーパターンのガイド

1. 概要

このチュートリアルでは、Martin Fowlerの本“Patterns of Enterprise Application Architecture”.で定義されているEnterprise Patternsの一部であるFront ControllerPatternをさらに深く掘り下げます。

Front Controllerは、「Webサイトに対するすべての要求を処理するコントローラー」として定義されます。 Webアプリケーションの前に立ち、リクエストを後続のリソースに委任します。 また、セキュリティ、国際化、特定のビューを特定のユーザーに提示するなどの一般的な動作へのインターフェイスも提供します。

これにより、アプリケーションは実行時にその動作を変更できます。 さらに、コードの重複を防ぐことにより、アプリケーションの読み取りと保守を支援します。

フロントコントローラーは、単一のハンドラーオブジェクトを介してリクエストをチャネリングすることにより、すべてのリクエスト処理を統合します。

2. 仕組み

Front Controller Patternは主に2つの部分に分けられます。 単一のディスパッチコントローラとコマンドの階層。 次のUMLは、汎用フロントコントローラー実装のクラス関係を示しています。

front-controller

この単一のコントローラーは、リクエストに関連付けられた動作をトリガーするために、コマンドにリクエストをディスパッチします。

その実装を示すために、コントローラーをFrontControllerServletに実装し、コマンドを抽象FrontCommandから継承されたクラスとして実装します。

3. セットアップ

3.1. Mavenの依存関係

まず、javax.servlet-apiを含む新しいMaven WARプロジェクトをセットアップします。


    javax.servlet
    javax.servlet-api
    4.0.0-b01
    provided

およびjetty-maven-plugin


    org.eclipse.jetty
    jetty-maven-plugin
    9.4.0.M1
    
        
            /front-controller
        
    

3.2. モデル

次に、ModelクラスとモデルRepositoryを定義します。 モデルとして次のBookクラスを使用します。

public class Book {
    private String author;
    private String title;
    private Double price;

    // standard constructors, getters and setters
}

これがリポジトリになります。具体的な実装のためにソースコードを検索するか、独自に提供することができます。

public interface Bookshelf {
    default void init() {
        add(new Book("Wilson, Robert Anton & Shea, Robert",
          "Illuminati", 9.99));
        add(new Book("Fowler, Martin",
          "Patterns of Enterprise Application Architecture", 27.88));
    }

    Bookshelf getInstance();

     boolean add(E book);

    Book findByTitle(String title);
}

3.3. FrontControllerServlet

サーブレット自体の実装は非常に簡単です。 リクエストからコマンド名を抽出し、コマンドクラスの新しいインスタンスを動的に作成して実行します。

これにより、Front Controllerのコードベースを変更せずに新しいコマンドを追加できます。

別のオプションは、静的な条件付きロジックを使用してサーブレットを実装することです。 これには、コンパイル時のエラーチェックの利点があります。

public class FrontControllerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
      HttpServletResponse response) {
        FrontCommand command = getCommand(request);
        command.init(getServletContext(), request, response);
        command.process();
    }

    private FrontCommand getCommand(HttpServletRequest request) {
        try {
            Class type = Class.forName(String.format(
              "com.example.enterprise.patterns.front."
              + "controller.commands.%sCommand",
              request.getParameter("command")));
            return (FrontCommand) type
              .asSubclass(FrontCommand.class)
              .newInstance();
        } catch (Exception e) {
            return new UnknownCommand();
        }
    }
}

3.4. FrontCommand

すべてのコマンドに共通の動作を保持するFrontCommandという抽象クラスを実装しましょう。

このクラスは、ServletContextとその要求および応答オブジェクトにアクセスできます。 さらに、ビューの解像度を処理します。

public abstract class FrontCommand {
    protected ServletContext context;
    protected HttpServletRequest request;
    protected HttpServletResponse response;

    public void init(
      ServletContext servletContext,
      HttpServletRequest servletRequest,
      HttpServletResponse servletResponse) {
        this.context = servletContext;
        this.request = servletRequest;
        this.response = servletResponse;
    }

    public abstract void process() throws ServletException, IOException;

    protected void forward(String target) throws ServletException, IOException {
        target = String.format("/WEB-INF/jsp/%s.jsp", target);
        RequestDispatcher dispatcher = context.getRequestDispatcher(target);
        dispatcher.forward(request, response);
    }
}

この抽象FrontCommandの具体的な実装は、SearchCommandになります。 これには、本が見つかった場合や本が見つからない場合の条件付きロジックが含まれます。

public class SearchCommand extends FrontCommand {
    @Override
    public void process() throws ServletException, IOException {
        Book book = new BookshelfImpl().getInstance()
          .findByTitle(request.getParameter("title"));
        if (book != null) {
            request.setAttribute("book", book);
            forward("book-found");
        } else {
            forward("book-notfound");
        }
    }
}

アプリケーションが実行されている場合は、ブラウザーでhttp://localhost:8080/front-controller/?command=Search&title=patternsを指定することで、このコマンドにアクセスできます。

SearchCommandは2つのビューに解決され、2番目のビューは次のリクエストhttp://localhost:8080/front-controller/?command=Search&title=any-titleでテストできます。

シナリオをまとめるために、すべての場合にフォールバックとして起動される2番目のコマンドを実装します。コマンド要求はサーブレットに認識されません。

public class UnknownCommand extends FrontCommand {
    @Override
    public void process() throws ServletException, IOException {
        forward("unknown");
    }
}

このビューは、http://localhost:8080/front-controller/?command=Order&title=any-titleで、またはURLパラメータを完全に除外することで到達可能になります。

4. 展開

WARファイルプロジェクトを作成することにしたので、Webデプロイメント記述子が必要になります。 このweb.xmlを使用すると、任意のサーブレットコンテナでWebアプリケーションを実行できます。



    
        front-controller
        
            com.example.enterprise.patterns.front.controller.FrontControllerServlet
        
    
    
        front-controller
        /
    

最後のステップとして、‘mvn install jetty:run'を実行し、ブラウザーでビューを検査します。

5. 結論

これまで見てきたように、Front Controller Patternと、サーブレットおよびコマンド階層としてのその実装に精通している必要があります。

いつものように、ソースon GitHubが見つかります。