Руководство по шаблону Front Controller в Java
1. обзор
В этом руководстве мы углубимся вFront ControllerPattern, частьEnterprise Patterns, как определено в книгеMartin Fowler“Patterns of Enterprise Application Architecture”..
Front Controller определяется как «контроллер, который обрабатывает все запросы к веб-сайту». Он стоит перед веб-приложением и делегирует запросы последующим ресурсам. Он также обеспечивает интерфейс для общего поведения, такого как безопасность, интернационализация и представление определенных представлений определенным пользователям.
Это позволяет приложению изменять свое поведение во время выполнения. Кроме того, это помогает читать и поддерживать приложение, предотвращая дублирование кода.
Front Controller консолидирует всю обработку запросов путем направления запросов через один объект-обработчик.
2. Как это работает?
Front Controller Pattern в основном делится на две части. Единый диспетчерский контроллер и иерархия команд. Следующий UML изображает отношения классов универсальной реализации Front Controller:
Этот единственный контроллер отправляет запросы командам для запуска поведения, связанного с запросом.
Чтобы продемонстрировать его реализацию, мы реализуем контроллер вFrontControllerServlet и команды как классы, унаследованные от абстрактногоFrontCommand.
3. Настроить
3.1. Maven Зависимости
Сначала мы создадим новый проектMaven WAR с включеннымjavax.servlet-api:
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 разрешается в два представления, второе представление можно протестировать с помощью следующего запроса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 мы можем запускать наше веб-приложение в любом контейнере сервлетов:
front-controller
com.example.enterprise.patterns.front.controller.FrontControllerServlet
front-controller
/
На последнем этапе мы запустим‘mvn install jetty:run' и проверим наши представления в браузере.
5. Заключение
Как мы уже видели, теперь мы должны быть знакомы сFront Controller Pattern и его реализацией в виде иерархии сервлетов и команд.
Как обычно, вы найдете источникиon GitHub.