Una guida al pattern del front controller in Java

1. Panoramica

In questo tutorial approfondiremo il Front Controller Pattern , parte degli Enterprise Patterns definiti nel libro di Martin Fowler "Patterns of Enterprise Application Architecture".

Front Controller è definito come "un controller che gestisce tutte le richieste di un sito Web". Si trova di fronte a un'applicazione web e delega le richieste alle risorse successive. Fornisce inoltre un'interfaccia per comportamenti comuni come sicurezza, internazionalizzazione e presentazione di viste particolari a determinati utenti.

Ciò consente a un'applicazione di modificare il proprio comportamento in fase di esecuzione. Inoltre aiuta a leggere e mantenere un'applicazione impedendo la duplicazione del codice.

Il Front Controller consolida tutta la gestione delle richieste canalizzando le richieste attraverso un unico oggetto gestore.

2. Come funziona?

Il pattern del front controller è principalmente diviso in due parti. Un unico controller di invio e una gerarchia di comandi. Il seguente UML descrive le relazioni di classe di un'implementazione generica del Front Controller:

Questo singolo controller invia le richieste ai comandi per attivare il comportamento associato a una richiesta.

Per dimostrare la sua implementazione, implementeremo il controller in un FrontControllerServlet e i comandi come classi ereditate da un FrontCommand astratto .

3. Configurazione

3.1. Dipendenze di Maven

Per prima cosa, configureremo un nuovo progetto Maven WAR con javax.servlet-api incluso:

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

così come jetty-maven-plugin :

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

3.2. Modello

Successivamente, definiremo una classe Model e un Repository del modello . Useremo la seguente classe Book come modello:

public class Book { private String author; private String title; private Double price; // standard constructors, getters and setters }

Questo sarà il repository, puoi cercare il codice sorgente per un'implementazione concreta o fornirne uno da solo:

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

L'implementazione del Servlet stesso è abbastanza semplice. Stiamo estraendo il nome del comando da una richiesta, creando dinamicamente una nuova istanza di una classe di comando e eseguendola.

Questo ci permette di aggiungere nuovi comandi senza modificare una base di codice del nostro Front Controller .

Un'altra opzione è implementare il servlet utilizzando una logica condizionale statica. Questo ha il vantaggio del controllo degli errori in fase di compilazione:

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.baeldung.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

Implementiamo una classe astratta chiamata FrontCommand , che mantiene il comportamento comune a tutti i comandi.

Questa classe ha accesso a ServletContext e ai suoi oggetti di richiesta e risposta. Inoltre, gestirà la risoluzione della vista:

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

Un'implementazione concreta di questo FrontCommand astratto sarebbe un SearchCommand . Ciò includerà la logica condizionale per i casi in cui è stato trovato un libro o quando manca un libro:

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

Se l'applicazione è in esecuzione, possiamo raggiungere questo comando puntando il nostro browser su // localhost: 8080 / front-controller /? Command = Search & title = patterns.

Il SearchCommand si risolve in due visualizzazioni, la seconda può essere testata con la seguente richiesta // localhost: 8080 / front-controller /? Command = Search & title = any-title.

Per completare il nostro scenario implementeremo un secondo comando, che viene attivato come fallback in tutti i casi, una richiesta di comando è sconosciuta al Servlet:

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

Questa vista sarà raggiungibile in // localhost: 8080 / front-controller /? Command = Order & title = any-title o tralasciando completamente i parametri URL .

4. Distribuzione

Poiché abbiamo deciso di creare un progetto di file WAR , avremo bisogno di un descrittore di distribuzione web. Con questo web.xml siamo in grado di eseguire la nostra applicazione web in qualsiasi contenitore Servlet:

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

Come ultimo passaggio eseguiremo "mvn install jetty: run" e ispezioneremo le nostre visualizzazioni in un browser.

5. conclusione

Come abbiamo visto finora, ora dovremmo avere familiarità con il Front Controller Pattern e la sua implementazione come Servlet e gerarchia di comandi.

Come al solito, troverai i sorgenti su GitHub.