Во второй статье о реализации Spring MVC в Java EE 6 мы возьмем метаданные, которые мы извлекли в первой части, и будем использовать их для вызова методов контроллера сопоставленных запросов в ответ на веб-запросы, а затем направим пользователя на веб-страницу в зависимости от результата. метода.
В этой статье мы будем реализовывать следующие функции, код для которых доступен для скачивания :
- Напишите сервлет, который будет отправлять запросы в наш класс обработчика MVC.
- В обработчике MVC мы возьмем веб-запрос и вызовем соответствующий метод контроллера.
- Возьмите результат из сопоставленного с запросом метода и разрешите его в представление, в которое пользователь перенаправляется
Реализация сервлета
Наш код сервлета принимает входящие запросы и делегирует их внедренному экземпляру MvcHandler.
@WebServlet(urlPatterns = "/demo/*") public class DelegatingServlet extends HttpServlet { @Inject private MvcHandler mvcHandler; protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doHandleRequest(req, resp, RequestMethod.GET); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doHandleRequest(req, resp, RequestMethod.POST); } private void doHandleRequest(HttpServletRequest request, HttpServletResponse response, RequestMethod requestMethod) { mvcHandler.handleRequest(request.getPathInfo(),requestMethod,request, response,getServletContext()); } }
Наш сервлет использует аннотацию @WebServlet, чтобы зарегистрировать сервлет для пути / demo / * url. Он внедряет экземпляр нашего класса MvcHandler и использует его для обработки запросов GET и POST. Когда мы вызываем обработчик MVC, мы должны передать несколько объектов, которые будут использоваться обработчиком. Забегая вперёд, мы увидим, что это будет расти, так как у нас есть методы контроллера, значения моделей, результаты и имена представлений для передачи, поэтому мы создадим новый объект с именем RequestContext, который будет хранить все эти вещи, и мы можем передать все эти предметы как один объект.
Это заставляет наши вызовы методов выглядеть лучше с меньшим количеством параметров, и нам не нужно постоянно добавлять параметры в методы для каждой новой необходимой информации. Работа с одним контекстом означает, что мы можем разбить обработчик до определенного набора шагов, при этом произведение каждого метода (то есть выборки значений модели) будет храниться в контексте и использоваться в следующем методе. Это также означает, что мы можем преобразовать его в интерфейс и / или абстрагировать некоторую информацию, доступную для обеспечения различных реализаций (например, версия портала). На данный момент нам просто нужна базовая версия с контекстом сервлета, объектами запроса и ответа. Нам также нужно сохранить контроллер, метод контроллера и результат этого метода.
public class RequestContext { private final ServletContext servletContext; private final HttpServletRequest request; private final HttpServletResponse response; private final RequestMethod requestMethod; private Object controller; private ControllerMethod method; private Object outcome; public RequestContext(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, RequestMethod requestMethod) { this.servletContext = servletContext; this.request = request; this.response = response; this.requestMethod = requestMethod; } //getters and setters omitted }
Реализация обработчика MVC
Возвращаясь к нашему обработчику MVC, код в этом классе собирает все вместе и организует вещи, когда он получает вызовы от сервлета.
public class MvcHandler { @Inject private ControllerInfo controllerInfo; @Inject private ControllerMethodMatcher matcher; @Inject private BeanManager beanManager; public void handleRequest(RequestContext context) { //find the controller method that best matches the request context.setMethod(matcher.findMatching(controllerInfo, context)); if (context.getMethod() != null) { Object controller = locateController(context.getMethod().getControllerClass()); context.setController(controller); // start executing the controller method executeControllerMethod(context); handleResponse(context); } else { throw new RuntimeException("Unable to find method for " + context.getRequestMethod() + " request with url " + context.getPath()); } } private void handleResponse(RequestContext context) { if (context.getOutcome() instanceof String) { String outcome = (String) context.getOutcome(); String view = "/"+outcome+".jsp"; context.forwardTo(view); } } private Object locateController(Class<?> controllerClass) { //returns a bean of type controllerClass from CDI } private void executeControllerMethod(RequestContext context) { //executes the controller method on the controller } }
Мы внедряем наш экземпляр ControllerInfo, который содержит все методы контроллера, и метод handleRequest () вызывается из сервлета при получении веб-запроса. Для обслуживания запроса необходимо выполнить следующие шаги:
- Найдите соответствующий метод контроллера для этого запроса.
- Найдите контроллер
- Выполните метод контроллера на этом экземпляре контроллера, сохранив результат, возвращенный методом.
- Обработайте правильный ответ обратно пользователю.
Пока что мы просто предполагаем, что метод контроллера возвращает строку, которая указывает имя представления, которое мы пересылаем. Я добавил метод для обработки пересылки в объекте контекста запроса.
Методы соответствия
ControllerMethodMatcher — это интерфейс, который можно использовать для поиска метода контроллера в списке методов контроллера, который соответствует информации входящего запроса, хранящейся в контексте запроса.
public interface ControllerMethodMatcher { ControllerMethod findMatching(ControllerInfo info,RequestContext context); }
Наша стандартная реализация этого пока очень проста. Мы перебираем наш список методов контроллера и проверяем, соответствует ли тип запроса методу запроса входящего запроса. Если это так, то если путь URL начинается с префикса уровня контроллера и заканчивается суффиксом уровня метода, то это совпадение. Поскольку мы уже отсортировали методы контроллера по порядку более крупных выражений, мы знаем, что первое совпадение, с которым мы столкнулись, на данный момент является лучшим.
public class DefaultControllerMatcher implements ControllerMethodMatcher { public ControllerMethod findMatching(ControllerInfo info,RequestContext context) { for (ControllerMethod method : info.getControllerMethods()) { if (matches(method, context)) { return method; } } return null; } protected boolean matches(ControllerMethod methodToTest, RequestContext context) { String path = context.getPath(); boolean result = methodToTest.matchesRequestMethod(context.getRequestMethod()) && (methodToTest.getPrefix() == null || path.startsWith(methodToTest.getPrefix())) && (methodToTest.getSuffix() == null || path.endsWith(methodToTest.getSuffix())); return result; } }
Мы можем переопределить этот класс и позже повторно реализовать метод совпадений, и, поскольку мы передадим RequestContext, у нас будет вся информация, доступная нам для определения наилучшего соответствия.
Выполнение метода контроллера
Мы можем просто использовать рефлексию для выполнения экземпляра метода контроллера. Мы предполагаем, что пока нет никаких параметров, которые будут позже.
private void executeControllerMethod(RequestContext context) { Method javaMethod = context.getMethod().getMethod(); Object outcome = null; try { outcome = javaMethod.invoke(context.getController()); } catch (Exception e) { e.printStackTrace(); } context.setOutcome(outcome); }
Видя это в действии
Наконец, мы можем собрать все это вместе, чтобы увидеть это в действии в веб-приложении. На данный момент наш код MVC находится в нашем веб-проекте, чтобы упростить его интеграцию. Позже мы можем извлечь код MVC в отдельный модуль для использования в качестве библиотеки в других проектах. Мы уже настроили контроллер и наш сервлет, теперь нам просто нужно создать пару страниц на основе строки, возвращаемой из сопоставленных методов. Если мы запустим приложение сейчас и перейдем по URL-адресу, такому как http: // localhost: 8080 / mvcdi / demo / person / list, мы получим сообщение об ошибке, поскольку listPeople.jsp не существует. Мы создадим следующие простые страницы JSP, чтобы мы могли что-то отобразить в браузере. Каждая страница состоит из стандартной структуры и некоторого текста, который указывает, на какой странице мы находимся.
<html> <head> <title>View Person</title> </head> <body> View Person </body> </html>
Мы делаем это для следующих страниц в корне веб-каталога.
- viewPerson.jsp
- listPeople.jsp
- updatedPerson.jsp
- editPerson.jsp (дополнительные изменения см. ниже)
Одна другая страница — это страница editPerson.jsp, на которой мы хотим разместить форму, содержащую только кнопку, чтобы мы могли выполнить запрос POST, который сопоставляется с методом на контроллере, который обрабатывает запрос POST со страницы editPerson.jsp, и переходит к обновленная страницаPerson.jsp в ответ.
<html> <head> <title>Editing Person</title> </head> <body> Editing Person <form method="post"> Some Form Here<br/> <input type="submit" value="Update" /> </form> </body> </html>
Если мы перейдем по адресу http: // localhost: 8080 / mvcdi / demo / person / edit и нажмем кнопку отправки, мы попадем на страницу, которая сообщает нам, что мы только что обновили кого-то, потому что это страница, возвращенная из метода контроллера для редактировать путь с помощью метода запроса POST.
Вы можете скачать ( mvcdi_part2.zip ) код для этого в виде проекта maven, который может работать на любом контейнере Java EE 6 и был протестирован как на JBoss AS 7, так и на Glassfish 3.1.1.
Это завершает вторую часть этой серии, посвященную реализации Spring MVC в Java EE 6 и CDI, и охватывает большую часть сопоставления запросов, поэтому мы можем направлять наши веб-запросы в методы контроллера и, в конечном счете, на конкретные страницы. В следующий раз мы рассмотрим предоставление данных модели на страницы.
С http://www.andygibson.net/blog/article/implementing-spring-mvc-in-cdi-and-java-ee-6-part-2/