Немного приличное веб-приложение состоит из комбинации шаблонов дизайна. Я упомяну только самые важные.
Базовым (архитектурным) шаблоном проектирования, который вы хотели бы использовать, является шаблон Model-View-Controller . Контроллер должен быть представлен сервлета , который (в) непосредственно создает / использует конкретный модель и View на основе запроса. Модель должны быть представлены классами JavaBeans. Это часто делится на бизнес-модель, которая содержит действия (поведение) и модель данных, которая содержит данные (информацию). View должен быть представлен JSP файлы , которые имеют прямой доступ к ( Data ) Модель Е.Л. (Expression Language).
Затем существуют вариации, основанные на том, как обрабатываются действия и события. Популярные из них:
MVC, основанный на запросах (действиях) : это самый простой для реализации. ( Бизнес ) Модель работает непосредственно HttpServletRequest
и HttpServletResponse
объекты. Вы должны собрать, преобразовать и проверить параметры запроса (в основном) самостоятельно. View может быть представлен простой ванили HTML / CSS / JS и не поддерживать состояние между запросами. Так работает Spring MVC , Struts и Stripes .
MVC на основе компонентов : это сложнее реализовать. Но в итоге вы получаете более простую модель и представление, в которых весь «сырой» API сервлетов полностью абстрагируется. Вам не нужно собирать, преобразовывать и проверять параметры запроса самостоятельно. Контроллер выполняет эту задачу и устанавливает собранный, преобразованные и подтвержденные параметры запроса в модели . Все, что вам нужно сделать, это определить методы действия, которые работают непосредственно со свойствами модели. Вид представлен «компонент» в аромате JSP или библиотеки тегов XML элементов , которые , в свою очередь , генерирует HTML / CSS / JS. Состояние зрениядля последующих запросов сохраняется в сеансе. Это особенно полезно для событий преобразования, проверки и изменения на стороне сервера. Вот как среди прочего JSF , Wicket and Play! работает.
В качестве дополнительного примечания, хобби с использованием доморощенного MVC-фреймворка - очень хорошее упражнение для обучения, и я рекомендую его, если вы сохраняете его для личных / личных целей. Но как только вы станете профессионалом, тогда настоятельно рекомендуется выбрать существующий фреймворк, а не изобретать свой собственный. Изучение существующей и хорошо разработанной структуры занимает в долгосрочной перспективе меньше времени, чем разработка и поддержание надежной структуры самостоятельно.
В приведенном ниже подробном объяснении я ограничусь запросом MVC, поскольку его легче реализовать.
Во-первых, часть Controller должна реализовывать шаблон Front Controller (который является специализированным типом шаблона Mediator ). Он должен состоять только из одного сервлета, который обеспечивает централизованную точку входа для всех запросов. Он должен создавать модель на основе информации, доступной по запросу, такой как pathinfo или servletpath, метод и / или конкретные параметры. Бизнес - модель называется Action
в приведенном ниже HttpServlet
примере.
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
}
else {
response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
}
}
catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}
Выполнение действия должно вернуть некоторый идентификатор, чтобы найти представление. Простейшим было бы использовать его в качестве имени файла JSP. Сопоставьте этот сервлет с конкретным url-pattern
в web.xml
, например /pages/*
, *.do
или даже просто *.html
.
В случае префиксов-моделей , как, например , /pages/*
вы могли бы затем вызвать URL как http://example.com/pages/register , http://example.com/pages/login , и т.д. , и обеспечить /WEB-INF/register.jsp
, /WEB-INF/login.jsp
с соответствующим GET и POST действий , Детали register
и login
т. Д. Затем доступны request.getPathInfo()
как в примере выше.
Когда вы используете суффикс-шаблоны, такие как *.do
, *.html
и т. Д., Тогда вы можете вызывать URL-адреса, такие как http://example.com/register.do , http://example.com/login.do и т. Д., И вы должны изменить примеры кода в этом ответе (также the ActionFactory
) для извлечения register
и login
частей request.getServletPath()
вместо.
Action
Должны следовать шаблону стратегии . Он должен быть определен как абстрактный / интерфейсный тип, который должен выполнять работу на основе переданных аргументов абстрактного метода (в этом отличие от шаблона Command , в котором абстрактный / интерфейсный тип должен выполнять работу на основе аргументы, которые были переданы при создании реализации).
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Вы можете сделать Exception
более конкретным с пользовательским исключением, как ActionException
. Это просто базовый пример, остальное зависит только от вас.
Вот пример того, LoginAction
который (как следует из его названия) входит в систему пользователя. Само по User
себе является моделью данных . View известно о присутствии User
.
public class LoginAction implements Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
return "home"; // Redirect to home page.
}
else {
request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
return "login"; // Go back to redisplay login form with error.
}
}
}
ActionFactory
Должны следовать шаблону Фабричный метод . По сути, он должен предоставлять креационный метод, который возвращает конкретную реализацию абстрактного / интерфейсного типа. В этом случае он должен возвращать реализацию Action
интерфейса на основе информации, предоставленной запросом. Например, метод и pathinfo (pathinfo - это часть после пути контекста и сервлета в URL запроса, исключая строку запроса).
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
В actions
свою очередь, это должен быть статический объект / приложение, Map<String, Action>
которое содержит все известные действия. Это зависит от вас, как заполнить эту карту. жестко прописывать:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
Или настраивается на основе файла конфигурации свойств / XML в classpath: (псевдо)
for (Entry entry : configuration) {
actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}
Или динамически на основе сканирования в пути к классам для классов, реализующих определенный интерфейс и / или аннотацию: (псевдо)
for (ClassFile classFile : classpath) {
if (classFile.isInstanceOf(Action.class)) {
actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
}
}
Имейте в виду, чтобы создать «ничего не делать» Action
для случая, когда нет отображения. Пусть, например, вернет непосредственно request.getPathInfo().substring(1)
тогда.
Другие образцы
Это были важные образцы до сих пор.
Чтобы продвинуться дальше, вы можете использовать шаблон Facade для создания Context
класса, который в свою очередь оборачивает объекты запроса и ответа и предлагает несколько удобных методов, делегирующих объектам запроса и ответа, и Action#execute()
вместо этого передает их в качестве аргумента в метод. Это добавляет дополнительный абстрактный слой, чтобы скрыть необработанный API сервлетов. Затем вы должны в итоге получить нулевые import javax.servlet.*
объявления в каждой Action
реализации. С точки зрения JSF, это то , что FacesContext
и ExternalContext
классы делают. Вы можете найти конкретный пример в этом ответе .
Затем есть шаблон State для случая, когда вы хотите добавить дополнительный уровень абстракции, чтобы разделить задачи сбора параметров запроса, их преобразования, проверки, обновления значений модели и выполнения действий. С точки зрения JSF, это то, что LifeCycle
делает.
Затем есть шаблон Composite для случая, когда вы хотите создать компонентное представление, которое может быть присоединено к модели и поведение которого зависит от состояния жизненного цикла на основе запроса. С точки зрения JSF, это то, что UIComponent
представляют.
Таким образом, вы можете постепенно развиваться в сторону компонентной структуры.
Смотрите также: