Глубокое понимание Spring MVC

Spring Boot Java задняя часть Spring
первоначальный проект

Используйте Spring Boot и web, тимелеаф-стартер для настройки исходного проекта. Конфигурация xml выглядит следующим образом:

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.0.1</version>    <relativePath/></parent><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-thymeleaf</artifactId>    </dependency></dependencies>скопировать код
Тестовые задания

Чтобы понять, как работает Spring Web MVC, сначала можно реализовать простую функцию входа в систему. Создать@Controllerкласс для измененияInternalController, этот класс содержит метод для обработки запросов GET.hello()Возвращает строку имени представления, интерпретируемую Spring. (в данном случае этоlogin.html)

@GetMapping("/")public String hello() {    return "login";}скопировать код

Чтобы обработать логику входа пользователя, создайте другой метод с данными входа, который принимает запрос POST. Затем верните страницу успеха или неудачи в соответствии с результатом обработки. Уведомление,login()Функция принимает доменный объект в качестве параметра и возвращаетModelAndViewобъект.

@PostMapping("/login")public ModelAndView login(LoginData loginData) {    if (LOGIN.equals(loginData.getLogin())       && PASSWORD.equals(loginData.getPassword())) {        return new ModelAndView("success",           Collections.singletonMap("login", loginData.getLogin()));    } else {        return new ModelAndView("failure",           Collections.singletonMap("login", loginData.getLogin()));    }}скопировать код

ModelAndViewСохраняются два разных объекта:

  • Модель: карта, используемая для отображения страницы.

  • Вид: страница шаблона.

Для удобства они объединены, чтобы метод контроллера мог возвращать оба. последнее использованиеThymeleafДействует как механизм шаблонов для рендеринга страниц.

Основы веб-приложений Java — сервлет

при вводе в браузереhttp://localhost:8080/, а затем нажмите Enter, что именно происходит, когда запрос достигает сервера? Как данные этого веб-запроса отображаются в браузере? Поскольку этот проект представляет собой простое приложение Spring Boot, к нему можно получить доступ черезSpring5ApplicationОсновной метод запускает проект. Spring Boot использует Apache Tomcat для запуска программы по умолчанию.После успешного запуска вы можете увидеть тот же журнал, что и ниже:

2018-04-10 20:36:11.626  INFO 57414 --- [main] 
  o.s.b.w.embedded.tomcat.TomcatWebServer  : 
  Tomcat initialized with port(s): 8080 (http)2018-04-10 20:36:11.634  INFO 57414 --- [main] 
  o.apache.catalina.core.StandardService   : 
  Starting service [Tomcat]2018-04-10 20:36:11.635  INFO 57414 --- [main] 
  org.apache.catalina.core.StandardEngine  : 
  Starting Servlet Engine: Apache Tomcat/8.5.23скопировать код

Поскольку Tomcat является контейнером сервлетов, почти все HTTP-запросы обрабатываются сервлетами Java. Естественная точка входа в Spring Web — это сервлет. Сервлет является основным компонентом всех веб-приложений Java; он очень низкого уровня и не предоставляет каких-либо конкретных шаблонов программирования, таких как MVC. HTTP-сервер может принимать только HTTP-запросы и возвращает ответ после обработки запроса. Последний API Servlet 3.0 больше не может использовать конфигурацию XML и может напрямую использовать конфигурацию Java.

Ядро Spring MVC — DispatcherServlet

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

  • Сопоставьте HTTP-запросы с соответствующими функциями обработчика.

  • Разбирать данные и заголовки HTTP-запроса в объекты DTO или домена.

  • Использование шаблона проектирования модель-представление-контроллер

  • Генерируйте ответы непосредственно из DTO, объектов домена и т. д.

ВеснаDispatcherServletПредоставляет вышеуказанные функции, является ядром среды Spring WEB MVC и основным компонентом приложения для приема всех запросов.DispatcherServletРасширяемость очень сильная. Например: он позволяет добавлять существующие или новые адаптеры для решения различных задач:

  • Сопоставьте запрос с классом или функцией, которая его обрабатывает (путемHandlerMappingвыполнить)

  • Используйте определенный шаблон для обработки запросов, например простой сервлет, сложный рабочий процесс MVC или просто метод. (Зависит отHandlerAdapterвыполнить)

  • Разрешает объекты представления по имени, позволяя вам использовать различные механизмы шаблонов, такие как XML, XSLT или другие технологии представления (путемViewResolverвыполнить)

  • По умолчанию компонент загрузки файлов Apache Commons используется для разбора загрузки файлов, или вы можете реализовать его самостоятельно.

  • Зависит отLocalResolverРеализуйте локализацию, включая файлы cookie, сеансы, HTTP-заголовок Accept или другие локализации, определенные пользователем.

Обработка HTTP-запросов

DispatcherServletСуществует длинная иерархия наследования. Необходимо понимать каждое отдельное понятие сверху вниз.

GenericServlet

GenericServlet是Часть спецификации сервлета, определяющаяservice()методы для приема запросов и возврата ответов.

public abstract void service(ServletRequest req, ServletResponse res)   throws ServletException, IOException;скопировать код

Все запросы с сервера будут вызывать этот метод.

HttpServlet

Как его имя,HttpServeltЯвляется реализацией HTTP-запроса в спецификации сервлета. Точнее,HttpServletэто реализацияservice()абстрактный класс. При разделении различных типов HTTP-запросов, которые должны обрабатываться разными функциями, реализация выглядит примерно так:

protected void service(HttpServletRequest req, HttpServletResponse resp)    throws ServletException, IOException {    String method = req.getMethod();    if (method.equals(METHOD_GET)) {        // ...        doGet(req, resp);    } else if (method.equals(METHOD_HEAD)) {        // ...        doHead(req, resp);    } else if (method.equals(METHOD_POST)) {        doPost(req, resp);        // ...    }скопировать код
HttpServletBean

в этих наследственных отношенияхHttpServletBeanэто первый весенний класс. Начальные параметры, полученные из web.xml или WebApplicationInitialzer для внедрения свойств компонента. Запросы в приложении вызывают doGet, doPost и другие методы соответственно для обработки различных HTTP-запросов.

FrameworkServlet

FrameworkServletДостигнутоApplicationContextAware, Интегрированный контекст веб-приложения. Однако он также может создавать собственный контекст приложения. Как упоминалось выше, родительский классHttpServletBeanПутем внедрения начальных параметров в качестве свойств bean-компонента. Итак, если имя класса контекста находится вcontextClassВ этом начальном параметре есть этот параметр для создания приложения экземпляр контекста, в противном случае используется значение по умолчаниюXmlWebApplicationContext. Поскольку конфигурация XML сейчас не является рекомендуемым способом настройки для Spring. Spring Boot использует по умолчаниюAnnotationConfigWebApplicationContextнастроитьDispatcherServlet.

DispatcherServlet: Унифицированная обработка запросов

HttpServlet.service()Отправка разных запросов к разным методам через типы HTTP-запросов хорошо реализована в базовом сервлете. Однако на уровне абстракции SpringMVC запросы не могут маршрутизироваться только по типу метода. такой же,FrameworkServletДругая основная функция заключается в использовании различной обработкиprocessRequest()соединить все вместе.

@Overrideprotected final void doGet(HttpServletRequest request,   HttpServletResponse response) throws ServletException, IOException {    processRequest(request, response);}@Overrideprotected final void doPost(HttpServletRequest request,   HttpServletResponse response) throws ServletException, IOException {    processRequest(request, response);}скопировать код
DispatcherServlet: добавьте полезную информацию в запрос

DispatcherServletвыполнитьdoService()метод. Он добавляет в запрос некоторые полезные объекты, а затем передает их в обработку веб-запроса, например: контекст веб-приложения, преобразователь локали, преобразователь темы, источник темы и т. д.

request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,   getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());скопировать код

в то же время,doService()Добавлена ​​флэш-карта ввода-вывода, базовый шаблон для передачи параметров из одного запроса в другой. Полезно в редиректах. (например, показ простого сообщения пользователю после перенаправления)

FlashMap inputFlashMap = this.flashMapManager  .retrieveAndUpdate(request, response);if (inputFlashMap != null) {    request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,       Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());скопировать код

тогдаdoService()позвонюdoDispatch()способ отправки запроса.

DispatcherServlet: Отправка запросов

dispatch()Основная цель — найти подходящий обработчик для обработки запроса и передачи параметров запроса/ответа. Обработчик может быть любым объектом и не ограничен конкретным интерфейсом. Это также означает, что Spring необходимо найти адаптер для использования этого обработчика. Чтобы найти подходящий обработчик для запроса, Spring просматривает реализациюHandlerMappingЗарегистрированная реализация интерфейса. Существует множество различных реализаций для удовлетворения наших различных потребностей.SimpleUrlHandlerMappingСопоставьте запросы с обработкой bean-компонентов с помощью URL-адресов.RequestMappingHandlerMappingВероятно, наиболее широко используемый картографический процессор. он сопоставляет запрос с@Controllerпод класс@RequestMappingспособ модификации. Это пример вышеhello()иlogin().

Обратите внимание, что два вышеуказанных метода@GetMappingи@PostMappingдекоративный. Эти две аннотации взяты из@RequestMapping.dispatch()Он также может обрабатывать некоторые другие задачи HTTP:

  • Если ресурс не существует, сократите запрос GET.

  • Используйте составной анализ для соответствующего запроса.

  • Если процессор предпочитает обрабатывать запрос асинхронно, замкните запрос.

обработать запрос

Теперь, когда Spring определил обработчик и адаптер обработчика для обработки запроса, пришло время обработать запрос. НижеHandlerAdapter.handle()подпись. Более важным моментом является то, что процессор может выбирать, как обрабатывать запрос:

  • Запишите ответ непосредственно в тело ответа и верните null

  • возвращаетDispatcherServletоказанныйModelAndViewобъект.

@NullableModelAndView handle(HttpServletRequest request,                     HttpServletResponse response,                     Object handler) throws Exception;скопировать код

Spring предоставляет множество типов обработчиков, следующиеSimpleControllerHandlerAdapterКак обрабатывать экземпляры контроллера Spring MVC (не путать с @Controller, вот класс).

public ModelAndView handle(HttpServletRequest request,   HttpServletResponse response, Object handler) throws Exception {    return ((Controller) handler).handleRequest(request, response);}скопировать код

ВторойSimpleServletHandlerAdapterОн адаптируется к обычному сервлету. сервлет не знаетModelAndView, обработайте запрос полностью самостоятельно и запишите возврат в соответствующее тело. Поэтому его адаптер просто возвращает null.

public ModelAndView handle(HttpServletRequest request,   HttpServletResponse response, Object handler) throws Exception {    ((Servlet) handler).service(request, response);    return null;}скопировать код

В этом примере контроллер создается@RequestMappingоформленный POJO, поэтому процессор будет использоватьHandlerMethodметод его инкапсуляции. Весеннее использованиеRequestMappingHandlerAdapterдля этого типа процессора.

Обработать аргументы, вернуть значение функции-обработчика

Обратите внимание, что обычно контроллер не получаетHttpServletRequestиHttpServletResponseВ качестве параметра, но может принимать и возвращать многие другие типы, такие как: объекты домена, параметры пути и т. д. Аналогичным образом, контроллер не обязан возвращатьModelAndViewпример. необязательно возвращать имя представления,ResponseEntityили POJO, который можно преобразовать в JSON.RequestMappingHandlerAdapterгарантировано отHttpServletRequestПараметры, требуемые методом синтаксического анализа вModelAndViewОбъект возвращен. Следующий кодRequestMappingHandlerAdapterГарантия этой вещи:

ServletInvocableHandlerMethod invocableMethod   = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {    invocableMethod.setHandlerMethodArgumentResolvers(      this.argumentResolvers);}if (this.returnValueHandlers != null) {    invocableMethod.setHandlerMethodReturnValueHandlers(      this.returnValueHandlers);}скопировать код

argumentResolversсуществуетHandlerMethodArgumentResolverВ примерах разные реализации. Существует более 30 различных реализаций парсеров параметров. Они могут анализировать параметры, необходимые функции, из параметров запроса. В том числе: переменные URL-адреса, параметры тела запроса, заголовки запроса, файлы cookie, сеанс и т. д.returnValueHandlersсуществуетHandlerMethodArgumentResolverВ примерах разные реализации. Существует также множество различных обработчиков возвращаемых значений для обработки результатов, возвращаемых методами, создаваяModelAndViewобъект. Например: когда функцияhello()При возврате строкиViewNameMethodReturnValueHandlerобработать это значение.login()вернутьModelAndViewобъект, Sring используетModelAndViewMethodReturnValueHandlerобработать это значение.

визуализировать вид

Теперь, когда Spring обработал HTTP-запрос, получитеModelAndViewНапример, теперь ему нужно отобразить HTML-страницу в браузере пользователя. Он опирается на модель, состоящую из модели и выбранного шаблона.ModelAndViewобъект. Точно так же Spring также может отображать JSON, XML или другие типы, принимаемые протоколом HTTP. Подробнее об REST вы узнаете в следующих главах. Вернитесь и посмотрите сейчасDispatcherServlet.render()первое использованиеLocaleResolverЭкземпляр устанавливает возвращенный Local. Сначала предположим, что в браузере правильно установлен заголовок Accetp. Использовать по умолчаниюAcceptHeaderLocaleResolverобрабатывать. Во время рендерингаModelAndViewМожет содержать имя представления или выбранного представления или ничего, если контроллер зависит от представления по умолчанию. теперь, когдаhello()иlogin()Метод указывает имя строки в качестве имени представления, поэтому вам нужно использовать viewResolvers, чтобы найти представление.

for (ViewResolver viewResolver : this.viewResolvers) {    View view = viewResolver.resolveViewName(viewName, locale);    if (view != null) {        return view;    }}скопировать код

Существует много реализаций ViewResolver, здесь мы используемthymeleaf-spring5который предоставилThymeleafViewResolverвыполнить. Анализатор знает, где искать представление, и предоставляет соответствующий экземпляр представления. вызов завершенrender()После этого Spring завершает задачу рендеринга HTML-страницы в браузере пользователя.