Перейти к основному содержимому

Руководство по шаблону переднего контроллера в Java

· 4 мин. чтения

1. Обзор

В этом руководстве мы углубимся в паттерн Front Controller Pattern , часть корпоративных паттернов , определенных в книге Мартина Фаулера «Шаблоны архитектуры корпоративных приложений».

Фронт-контроллер определяется как «контроллер, который обрабатывает все запросы к веб-сайту». Он стоит перед веб-приложением и делегирует запросы последующим ресурсам. Он также предоставляет интерфейс для общего поведения, такого как безопасность, интернационализация и представление конкретных представлений определенным пользователям.

Это позволяет приложению изменять свое поведение во время выполнения. Кроме того, это помогает читать и поддерживать приложение, предотвращая дублирование кода.

Front Controller объединяет всю обработку запросов, направляя запросы через один объект-обработчик.

2. Как это работает?

Шаблон переднего контроллера в основном разделен на две части. Единый диспетчерский контроллер и иерархия команд. Следующий UML изображает отношения классов универсальной реализации Front Controller:

./c1f11493449f6ba5c37d515fbdfdd99c.png

Этот единственный контроллер отправляет запросы командам, чтобы инициировать поведение, связанное с запросом.

Чтобы продемонстрировать его реализацию, мы реализуем контроллер в FrontControllerServlet и команды как классы, унаследованные от абстрактного FrontCommand .

3. Настройка

3.1. Зависимости Maven

Во- первых, мы настроим новый проект Maven WAR с включенным javax.servlet-api :

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0-b01</version>
<scope>provided</scope>
</dependency>

а также причал-maven-плагин :

<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.0.M1</version>
<configuration>
<webApp>
<contextPath>/front-controller</contextPath>
</webApp>
</configuration>
</plugin>

3.2. Модель

Далее мы определим класс Model и репозиторий моделей . В качестве модели мы будем использовать следующий класс 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();

<E extends Book> boolean add(E book);

Book findByTitle(String title);
}

3.3. Фронтконтроллерсервлет

Реализация самого сервлета довольно проста. Мы извлекаем имя команды из запроса, динамически создаем новый экземпляр класса команды и выполняем его.

Это позволяет нам добавлять новые команды без изменения базы кода нашего 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.foreach.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 разрешается в два представления, второе представление можно протестировать с помощью следующего запроса http://localhost:8080/front-controller/?command=Search&title=any-title .

Чтобы завершить наш сценарий, мы реализуем вторую команду, которая запускается как резервная во всех случаях, когда запрос команды неизвестен сервлету:

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.xml мы можем запустить наше веб-приложение в любом контейнере сервлетов:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>front-controller</servlet-name>
<servlet-class>
com.foreach.enterprise.patterns.front.controller.FrontControllerServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>front-controller</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

В качестве последнего шага мы запустим «mvn install jetty:run» и проверим наши представления в браузере.

5. Вывод

Как мы уже видели, теперь мы должны быть знакомы с шаблоном Front Controller и его реализацией в виде сервлета и иерархии команд.

Как обычно, вы найдете исходники на GitHub .