Напишите SpringMVC для серии Spring

Java

содержание

введение

В предыдущих главах мы просто завершили простую версию Spring, которая уже включает такие функции, как контейнер, внедрение зависимостей, АОП и разбор файла конфигурации. В этом разделе мы реализуем наш собственный springMvc.

О MVC/SpringMVC

SpringMvc — это веб-фреймворк, основанный на шаблоне mvc. SpringMVC framework — это тип, который предоставляет архитектуру MVC (Model-View-Controller) и компоненты для разработки гибких и слабо связанных веб-приложений.

Шаблон MVCРазделяет разные части приложения, обеспечивая слабую связь между этими элементами.

  • Модель (Model) инкапсулирует данные приложения, обычно относится к обычным bean-компонентам.
  • Представление отвечает за визуализацию данных модели и, как правило, генерирует выходные данные в формате HTML, которые клиентский браузер может интерпретировать.
  • Контроллер (Controller) отвечает за обработку пользовательских запросов и получение результатов запросов, передавая их в представление для рендеринга.

SpringMVC

SpringMVC обрабатывает поток запросов

Во-первых, давайте посмотрим, что делает SpringMVC во всем процессе обработки http-запросов.

SpringMVC请求处理流程

//Картинка взята из интернета

Из приведенного выше рисунка мы можем обобщить поток обработки springmvc:

  1. Клиент отправляет запрос, который перехватывается веб-контейнером (tomcat и т. д.), а веб-контейнер передает запрос DispatcherServlet.
  2. После того, как DispatcherServlet получит запрос, он передаст запрос HandlerMapping (сопоставитель обработчиков), чтобы найти обработчик (обработчик), соответствующий запросу. На самом деле этот процесс разделен на две части на рисунке, потому что URL-адрес запроса может иметь несколько обработчиков запросов, например, запросы GET, запросы POST и т. д. обрабатываются разными объектами процессора, поэтому требуется HandlerAdapter (адаптер процессора) для получить соответствующий объект процессора в соответствии с различными параметрами запроса.
  3. После получения запрошенного объекта обработчика выполните поток обработки запроса обработчика. Поток обработки здесь обычно представляет собой бизнес-процесс, который мы определяем при разработке.
  4. После выполнения потока обработки возвращенный результат упаковывается как объект ModelAndView и возвращается в DispatcherServlet.
  5. DispatcherServlet разрешает ModelAndView для просмотра через ViewResolver (преобразователь представления).
  6. Визуализируйте страницу через представление и ответьте пользователю.

Выше приведен процесс выполнения HTTP-запроса SpringMVC от начала и завершения ответа.Наш SpringMVC не так сложен, как есть на самом деле, но соответствующие функции будут реализованы.

Анализ SpringMVC

Мы знаем, что SpringMVC — это веб-фреймворк, реализующий шаблон MVC, поэтому должен бытьModel,View,Controllerтри роли. На приведенном выше рисунке мы также видим очень важный класс: DispatcherServlet. Далее мы анализируем его отдельно.

Controller

Контроллер (Controller) отвечает за обработку пользовательских запросов и получение результатов запросов, передавая их в представление для рендеринга.

В SpringMVC контроллер отвечает за обработку запросов, отправляемых DispatcherServlet, и преобразование результатов запроса после бизнес-обработки в модель для использования в представлении. Сопоставление запроса с соответствующим контроллером в SpringMVC предоставляет нам два разных метода:

  1. Сопоставление на уровне экземпляра, каждый запрос имеет соответствующий экземпляр класса для обработки, аналогично Struts2. Этот метод редко используется на практике. Для реализации этого типа вам необходимо реализовать интерфейс контроллера.
  2. Сопоставление на уровне методов, запросы сопоставляются с методами bean-компонента, так что каждый контроллер может соответствовать нескольким запросам, и проще обеспечить потокобезопасность параллельных запросов.
сопоставление на уровне экземпляра

Думаете, как реализовать сопоставление на уровне экземпляра?

В сопоставлении на уровне экземпляра каждый запрос соответствует другому классу, а именно一个URL<==>一个Class, чтобы мы могли сопоставить beanName с адресом запроса.

В то же время наша структура должна предоставить пользователям запись обработки запросов для реализации бизнес-кода. Здесь мы определяем интерфейс контроллера, который предоставляет метод обработки.

Controller

Интерфейс содержит метод обработки handlerRequest, и всем контроллерам уровня экземпляра требуется интерфейс контроллера. Завершите бизнес-логику в методе handlerRequest. Поскольку мы не можем судить о типе значения, которое должно быть возвращено после завершения бизнес-логики, вместо этого мы используем Object.

Чтобы определить, что здесь должен возвращать метод, мы должны сначала понять, для чего используется возвращаемое значение? Возвращаемое значение содержит результат бизнес-обработки. И вернуться на страницу для рендеринга страницы. Поэтому он должен содержать значение результата и конкретную страницу, которую необходимо вернуть. Учитывая, что возвращаемое значение является не только результатом бизнес-обработки, возможно, пользователю нужно установить какие-то другие значения на странице, мы определяем тип карты для получения.

ModelAndView

Добавлен метод hasView(), потому что мы возвращаем не обязательно информацию о странице, например возвращаемое значение json.

Работа представления очень проста, то есть оно отвечает браузеру возвращаемым нами значением. Таким образом, интерфейс представления выглядит следующим образом:

View

сопоставление на уровне метода

Тем, кто использовал SpringMVC, совершенно ясно, что описанный выше метод сопоставления на уровне экземпляра в основном не используется. Сопоставление на уровне экземпляра, если в проекте слишком много запросов, приведет к слишком большому количеству классов в проекте. Обычно мы чаще используем сопоставления на уровне методов.

Наше сопоставление на уровне метода здесь не требует реализации интерфейса контроллера, здесь мы имитируем SpringMVC для использования@Controllerдля представления контроллера используйте@RequestMappingдля представления различных запросов.

annotation

RequestMethod относится к типу http-запроса, включаяGEt,POST,PUTРавный тип, тип перечисления.

Обработка сопоставления методов аналогична сопоставлению экземпляров, за исключением того, что оно сопоставляется с методом.

распространение запроса

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

请求分发

Обработчики запросов, соответствующие разным типам отображений, определенно отличаются, например, отображение экземпляра реализовано за счет реализации интерфейса Controller, обработка тоже идет об интерфейсе, а отображение метода реализовано через аннотации. То есть разные методы, разные методы сопоставления и разные обработчики запросов.

Простой метод состоит в том, чтобы определить различные типы процессоров отдельно, а затем определить конкретный процессор на основании суждения в DispatcherServlet. Эта идея обработки очень проста, но проблема в том, что если мы хотим добавить еще один метод обработки, то исходный код нужно изменить, что явно нарушает принцип открытого-закрытого, а также принесет проблемы с обслуживанием кода. Поэтому мы надеемся, что у DispatcherServlet есть способ избежать этого изменения.

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

HandlerMapping

Мы определяем интерфейс для отображения обработчика запросов, Функция этого интерфейса состоит в том, чтобы получить конкретный обработчик запроса для запроса, и различные методы обработки реализуют этот интерфейс соответственно.

handlerMapping

BeanNameUrlHandlerMapping — это обработчик сопоставления экземпляров, а RequestMappingHandlerMapping — обработчик сопоставления методов. И как сопоставить запрос с HandlerMapping? Все, о чем мы можем думать, это URL-адрес, здесь мы определяем urlMaps для хранения соответствия между URL-адресом и сопоставителем процессора.

HandlerAdapter

Теперь, когда у нас есть HandlerMapping, мы можем получить объект обработки определенного типа метода обработки. Но на самом деле у нас все еще нет фактического обработчика, поэтому нам также нужно получить фактический обработчик в соответствии с запросом.Здесь мы определяем HandlerAdapter для получения фактического обработчика.

handlerAdapter

handler(...) — это конкретный метод обработки, который на самом деле является контроллером выполнения, и поддержка в основном используется для определения того, является ли он объектом-обработчиком.

Здесь очевидно, что для отображения экземпляра нам нужно только выполнить метод handlerRequest(...) в методе, но это не так просто для отображения метода.Различные методы основаны на@RequestMappingПредставляет другой запрос. Поэтому нам также нужен класс для представления различной информации о методах, чтобы его можно было использовать сразу после передачи запроса.

requestMappinginfo

в определении классаclassRequestMappingозначает действие над классом@RequestMapping,methodRequestMappingозначает действие по методу@RequestMapping,methodПредставляет информацию о методе, действующем на класс.match(...)Метод используется для проверки соответствия текущего запроса этому RequestMappingInfo.

регистрация сканирования

В основном наши приготовления закончены, теперь нам нужно подумать о том, как идентифицировать наш контроллер и сгенерироватьRequestMappingInfoИнформация.

В первый раз создать — инициализировать эту информацию при запуске проекта, потому что информация будет использована сразу после запроса. И где происходит акт инициализации этой информации? Моей первой реакцией было датьDispatcherServlet, потому что интуитивно он используется.На самом деле, реальное использование этой информации - это класс реализации HandlerMapping, который получает фактический процессор с помощью такой информации, как запрос и RequestMappingInfo, поэтому информация об инициализации должна быть передана классу реализации HandlerMapping. .

Что нам нужно понять Извлечение компонента, аннотированного с помощью Controller, или класса, реализующего интерфейс класса Controller, должно быть выполнено после инициализации компонента, поэтому нам необходимо предоставить интерфейс для получения типа контроллера после инициализации. При этом получить уже инициализированный класс, тогда он точно будет использоватьсяApplicationContext. сейчас мыRequestMappingИзмените интерфейс.

image

существуетafterPropertiesSet()метод для получения bean-компонента типа контроллера. В коде, который мы завершили ранее, предоставляется только метод получения bean-компонентов по beanName, поэтому здесь нам также необходимо предоставить метод для получения всех типов выполнения.

public void afterPropertiesSet() {
    String[] beanNameForType = applicationContext.getBeanNameForType(Object.class);
    for(String beanName:beanNameForType){
        Class type = applicationContext.getType(beanName);
        //判断是否是控制器类型
        if (isHandler(type)) {
            //注册控制器的类型
            detectHandlerMethod(type);
        }
    }
}

DispatcherServlet

Ну вот, подготовка к контроллеру почти завершена, теперь нужно реализоватьDispatcherServlet.

отDispatcherServletИз названия мы знаем, что это сервлет, наш фреймворк основан на сервлете, и сам фреймворк SpringMVC тоже основан на сервлете. Конечно, это можно реализовать и по другим технологиям, например Struts2 на базе Filter.

Давайте сначала посмотримDispatcherServletЗадачи, которые необходимо выполнить:

  1. Создайте объект-контейнер ApplicationContext
  2. получить из контейнераHandlerMapping,HandlerAdapterобъект.
  3. запрос на распространение
  4. просмотреть переадресацию

Те, кто знаком с сервлетами, должны знать, что сервлеты предоставляют ряд API-интерфейсов жизненного цикла, и вышеуказанные вещи необходимо выполнять на разных этапах жизненного цикла сервлета.

  • Инициализация и получение объектов-контейнеровHandlerMapping,HandlerAdapterОбъекты создаются в init(...).
  • Распределение запросов поservice(HttpServletRequest req, HttpServletResponse res)Заканчивать.
  • destroy()Полная обработка после закрытия.

DispatcherServlet

View

Когда мы ранее определяли контроллер, мы упомянули, что контроллер возвращает объект ModelAndView, который определяет, какую страницу возвращать, и данные результата обработки. Таким образом, необходимо предоставить не только объект ModelAndView, но и объект View. Теперь мы надеемся, что этот процесс может быть максимально простым. Пользователь может только указать имя представления, а затем фреймворк может автоматически найти соответствующую страницу и отобразить ее.

Теперь нам нужно переопределить класс ModelAndViewView.

ModelAndView

Таким образом, пользователь может передать имя, а затем HandlerAdapter сгенерирует ModelAndView в соответствии с переданным обработчиком, а также может настроить объект ModelAndView.

ViewResolver

Когда наша предыдущая подготовка завершена, это не означает, что ее можно завершить, потому что могут быть разные операции для разных представлений, например, прямая переадресация на URL-адрес, возможно, перенаправление на другой URL-адрес или прямой возврат строки json. Поэтому нам также необходимо определить различные преобразователи представлений для преобразования ModelAndView в соответствующее представление.

ViewResolver

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

После определения преобразователя представлений нам также необходимо определить несколько классов представлений для обработки различных ситуаций.

View

Тип View здесь также может добавлять другие типы процессоров в соответствии с различными потребностями, такими как freemarker, JSTL и т. д. Для обработки json нам также необходимо определить@ResponseBodyаннотации. Когда эта аннотация используется, мы преобразуем возвращаемое значение в строку JSON и возвращаем ее непосредственно клиенту через ответ.

резюме

На этом SpringMVC в основном закончился, в общем, содержание этой статьи немного более хлопотное. Основная причина в том, что здесь задействовано много контента, и это время относительно занято, поэтому я обычно трачу время, чтобы разобраться с делами после окончания работы.В настоящее время я только в основном закончил свои мысли. Код также просто организует фрейм, а содержимое не заполняется. Поэтому в статье могут быть ошибки, если вы их найдете, то можете указать на них. Позже будет время реализовать код. Весь код здесь:Spring.

Суммировать

Рукописный фреймворк Spring почти такой же.Эту серию статей я написал, чтобы закрепить результаты моего изучения Spring.Конечно, было бы лучше, если бы я мог помочь всем изучить Spring. Содержание Spring очень сложное и включает в себя много контента.Эта серия статей может только помочь вам иметь начальное понимание принципа Spring.В процессе просмотра исходного кода Spring вы не будете полностью запутаны . Из-за технического уровня в статье могут быть некоторые ошибки, которые вы можете указать.