Eine Anleitung zum Front Controller Pattern in Java

Eine Anleitung zum Front Controller Pattern in Java

1. Überblick

In diesem Tutorial werden wir uns eingehender mit denFront ControllerPattern befassen, einem Teil derEnterprise Patterns, wie inMartin Fowler Buch“Patterns of Enterprise Application Architecture”. definiert

Front Controller ist definiert als "ein Controller, der alle Anforderungen für eine Website verarbeitet". Es steht vor einer Webanwendung und delegiert Anforderungen an nachfolgende Ressourcen. Es bietet auch eine Schnittstelle zu allgemeinen Verhaltensweisen wie Sicherheit, Internationalisierung und die Darstellung bestimmter Ansichten für bestimmte Benutzer.

Dies ermöglicht einer Anwendung, ihr Verhalten zur Laufzeit zu ändern. Darüber hinaus hilft es, eine Anwendung zu lesen und zu pflegen, indem das Kopieren von Code verhindert wird.

Der Front Controller konsolidiert die gesamte Anforderungsbearbeitung, indem er Anforderungen über ein einziges Handlerobjekt kanalisiert.

2. Wie funktioniert es?

DasFront Controller Pattern ist hauptsächlich in zwei Teile unterteilt. Ein einzelner Dispatching-Controller und eine Befehlshierarchie. Die folgende UML zeigt Klassenbeziehungen einer generischen Front Controller-Implementierung:

front-controller

Dieser einzelne Controller sendet Anforderungen an Befehle, um das mit einer Anforderung verbundene Verhalten auszulösen.

Um die Implementierung zu demonstrieren, implementieren wir den Controller inFrontControllerServlet und Befehle als Klassen, die von einem abstraktenFrontCommand geerbt wurden.

3. Konfiguration

3.1. Maven-Abhängigkeiten

Zuerst richten wir ein neuesMaven WAR-Projekt mitjavax.servlet-api ein:


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

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

3.2. Modell

Als Nächstes definieren wir eineModel-Klasse und ein ModellRepository. Wir werden die folgendeBook-Klasse als Modell verwenden:

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

    // standard constructors, getters and setters
}

Dies ist das Repository. Sie können den Quellcode zur konkreten Implementierung nachschlagen oder einen eigenen Code bereitstellen:

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

Die Implementierung des Servlets selbst ist recht einfach. Wir extrahieren den Befehlsnamen aus einer Anforderung, erstellen dynamisch eine neue Instanz einer Befehlsklasse und führen sie aus.

Dies ermöglicht es uns, neue Befehle hinzuzufügen, ohne die Codebasis unsererFront Controller zu ändern.

Eine weitere Möglichkeit besteht darin, das Servlet mithilfe einer statischen, bedingten Logik zu implementieren. Dies hat den Vorteil einer Fehlerüberprüfung während der Kompilierung:

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

Implementieren wir eine abstrakte Klasse namensFrontCommand, die das allen Befehlen gemeinsame Verhalten enthält.

Diese Klasse hat Zugriff auf dieServletContext und ihre Anforderungs- und Antwortobjekte. Darüber hinaus wird die Ansichtsauflösung behandelt:

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);
    }
}

Eine konkrete Implementierung dieser ZusammenfassungFrontCommand wäreSearchCommand. Dies schließt bedingte Logik für Fälle ein, in denen ein Buch gefunden wurde oder wenn ein Buch fehlt:

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");
        }
    }
}

Wenn die Anwendung ausgeführt wird, können wir diesen Befehl erreichen, indem wir unseren Browser aufhttp://localhost:8080/front-controller/?command=Search&title=patterns richten.

DieSearchCommand werden in zwei Ansichten aufgelöst, die zweite Ansicht kann mit der folgenden Anforderunghttp://localhost:8080/front-controller/?command=Search&title=any-title getestet werden.

Um unser Szenario abzurunden, implementieren wir einen zweiten Befehl, der in allen Fällen als Fallback ausgelöst wird. Eine Befehlsanforderung ist dem Servlet unbekannt:

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

Diese Ansicht ist beihttp://localhost:8080/front-controller/?command=Order&title=any-title oder durch vollständiges Auslassen derURL-Parameter erreichbar.

4. Einsatz

Da wir uns entschieden haben, einWAR-Dateiprojekt zu erstellen, benötigen wir einen Webbereitstellungsdeskriptor. Mit diesenweb.xml können wir unsere Webanwendung in jedem Servlet-Container ausführen:



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

Als letzten Schritt führen wir‘mvn install jetty:run' aus und überprüfen unsere Ansichten in einem Browser.

5. Fazit

Wie wir bisher gesehen haben, sollten wir jetzt mit denFront Controller Pattern und ihrer Implementierung als Servlet- und Befehlshierarchie vertraut sein.

Wie üblich finden Sie die Quellenon GitHub.