1. Введение в SpringMVC
1.1 Что такое Spring Web MVC
Spring Web MVC — это облегченная веб-инфраструктура на основе Java, которая реализует шаблон проектирования Web MVC типа, управляемого запросом.Он использует идею шаблона архитектуры MVC для разделения обязанностей веб-уровня.Основываясь на управляемом запросом, он означает использование модели «запрос-ответ», цель фреймворка — помочь нам упростить разработку, а Spring Web MVC — также упростить нашу повседневную веб-разработку. В традиционной технологической системе Jsp/Servlet, если мы хотим разработать интерфейс, один интерфейс соответствует одному сервлету, что приведет к разработке множества сервлетов Использование SpringMVC может эффективно упростить этот шаг.
Spring Web MVC также является реализацией шаблона Service-to-Worker, но его можно оптимизировать. Интерфейсным контроллером является DispatcherServlet; контроллер приложения может быть разделен на сопоставление обработчиков для управления процессором и преобразователь представлений для управления представлениями; контроллер/действие/процессор страницы представляет собой интерфейс контроллера (содержит только ModelAndView handleRequest(запрос, ответ) метод, также известный как Handler) реализация (также может быть любым классом POJO); поддерживает синтаксический анализ локали (Locale), синтаксический анализ темы (Theme) и загрузку файлов и т. д.; обеспечивает очень гибкую проверку данных, форматирование и механизмы привязки данных; обеспечивает мощную контрактную поддержку программирования (конвенция-сначала принцип).
1.2 Что Spring Web MVC может сделать для нас
- Позволяет нам очень легко создавать чистые веб-слои и тонкие веб-слои;
- Разработка более лаконичного веб-слоя;
- Нативно интегрирован с инфраструктурой Spring (например, контейнером IoC, AOP и т. д.);
- Обеспечивает мощную поддержку контрактного программирования, где контракт больше, чем конфигурация;
- Простое модульное тестирование веб-слоя;
- Поддержка гибкого сопоставления URL-адреса с контроллером страницы;
- Его очень легко интегрировать с другими технологиями просмотра, такими как Velocity, FreeMarker и т. д., потому что данные модели размещаются не в конкретном API, а в модели (реализована структура данных карты, поэтому ее легко использовать другими фреймворками);
- Очень гибкий механизм проверки данных, форматирования и привязки данных, может использовать любой объект для привязки данных без необходимости реализации определенного API-интерфейса;
- Предоставьте набор мощных библиотек тегов JSP для упрощения разработки JSP;
- Поддержка гибкой локализации, тематического и другого парсинга;
- упрощенная обработка исключений;
- Поддержка статических ресурсов;
- Поддержка стиля RESTful
2. HelloWorld
Затем познакомьтесь с SpringMVC на простом примере.
1. Используйте Maven для создания веб-проекта (см. руководство по Maven). 2. В файле pom.xml добавьте зависимость spring-webmvc:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
После добавления зависимости spring-webmvc добавляются другие spring-web, spring-aop, spring-context и т. д.
3. Подготовьте контроллер — интерфейс, который обрабатывает запросы браузера.
public class MyController implements Controller {
/**
* 这就是一个请求处理接口
* @param req 这就是前端发送来的请求
* @param resp 这就是服务端给前端的响应
* @return 返回值是一个 ModelAndView,Model 相当于是我们的数据模型,View 是我们的视图
* @throws Exception
*/
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
ModelAndView mv = new ModelAndView("hello");
mv.addObject("name", "javaboy");
return mv;
}
}
Контроллер, который мы здесь создали, является внешним интерфейсом обработки запросов.
4. Создайте представление
Здесь мы используем jsp в качестве представления и создаем файл hello.jsp в каталоге webapp со следующим содержимым:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>hello ${name}!</h1>
</body>
</html>
5. В каталоге ресурсов создайте файл конфигурации springmvc с именем spring-servlet.xml Здесь мы сначала напишем простую демонстрацию, поэтому нам не нужно сначала добавлять конфигурацию spring.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.javaboy.helloworld.MyController" name="/hello"/>
<!--这个是处理器映射器,这种方式,请求地址其实就是一个 Bean 的名字,然后根据这个 bean 的名字查找对应的处理器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" id="handlerMapping">
<property name="beanName" value="/hello"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" id="handlerAdapter"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
6. Загрузите файл конфигурации springmvc
При запуске веб-проекта загружается файл конфигурации springmvc, что делается в файле web.xml.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Все запросы будут автоматически перехвачены.После перехвата запрос будет передан DispatcherServlet для обработки.При загрузке DispatcherServlet необходимо указать путь к конфигурационному файлу. Здесь действует правило по умолчанию, если конфигурационный файл находится в каталоге webapp/WEB-INF/, а имя конфигурационного файла равно имени DispatcherServlet +-servlet
(то есть путь к конфигурационному файлу здесь — webapp/WEB-INF/springmvc-servlet.xml), в этом случае вы можете добавить параметр init-param без ручной настройки конфигурационного файла springmvc, и фреймворк автоматически загрузить его.
7. Настройте и запустите проект (см. руководство по Maven)
8. После успешного запуска проекта браузерhttp://localhost:8080/helloВы можете увидеть следующую страницу:
3. Рабочий процесс SpringMVC
Во время интервью более 99% вопросов о SpringMVC — это вопрос.
4. Компоненты в SpringMVC
1. DispatcherServlet: передний контроллер
Когда запрос пользователя поступает на фронт-контроллер, это эквивалентно c в режиме mvc.DispatcherServlet — это центр управления всем процессом, эквивалент мозга SpringMVC, который вызывает другие компоненты для обработки запроса пользователя.Существование DispatcherServlet уменьшает взаимодействие между компонентами.
2.HandlerMapping: сопоставитель обработчиков
HandlerMapping отвечает за поиск обработчика или процессора (то есть того, что мы называем контроллером) в соответствии с запросами пользователя.SpringMVC предоставляет разные преобразователи для реализации различных методов отображения, таких как: метод файла конфигурации, метод интерфейса, метод аннотации и т. д., в действительности мы обычно используем метод аннотации.
3.Handler: обработчик
Обработчик — это внутренний контроллер, следующий за внешним контроллером DispatcherServlet.Под управлением DispatcherServlet обработчик обрабатывает определенные запросы пользователей. Поскольку обработчик включает в себя конкретные бизнес-запросы пользователей, от программистов обычно требуется разрабатывать обработчики в соответствии с бизнес-требованиями. (Упомянутый здесь обработчик относится к нашему контроллеру)
4.HandlAdapter: адаптер обработчика
Обработчик выполняется через HandlerAdapter, который является приложением режима адаптера, и путем расширения адаптера можно выполнять другие типы обработчиков.
5.ViewResolver: Преобразователь просмотра
ViewResolver отвечает за создание представления представления из результата обработки.ViewResolver сначала преобразует имя логического представления в имя физического представления, то есть конкретный адрес страницы, затем генерирует объект представления представления и, наконец, визуализирует представление для отображения. результат обработки пользователю через страницу. Платформа SpringMVC предоставляет множество типов представлений View, в том числе: jstlView, freemarkerView, pdfView и т. д. В обычных условиях необходимо отображать данные модели для пользователей с помощью тегов страниц или технологии шаблонов страниц, а программистам необходимо разрабатывать определенные страницы в соответствии с бизнес-требованиями.
5. DispatcherServlet
5.1 Функция DispatcherServlet
DispatcherServlet является реализацией шаблона проектирования фронт-контроллера, обеспечивает централизованную точку доступа к Spring Web MVC, отвечает за распределение обязанностей и легко интегрируется с контейнером Spring IoC, чтобы получить все преимущества Spring. DispatcherServlet в основном используется для планирования ответственности и сам в основном используется для управления процессом.Основные обязанности заключаются в следующем:
- Парсинг выгрузки файлов, если тип запроса составной, парсинг выгрузки файлов будет выполняться через MultipartResolver;
- С помощью HandlerMapping запрос сопоставляется с процессором (возвращает HandlerExecutionChain, который включает в себя процессор и несколько перехватчиков HandlerInterceptor);
- Поддержка нескольких типов обработчиков (обработчиков в HandlerExecutionChain) через HandlerAdapter;
- Разобрать имя логического представления на конкретную реализацию представления с помощью ViewResolver;
- анализ локализации;
- Отрисовка конкретных видов и т.д.;
- Если во время выполнения возникает исключение, оно будет передано HandlerExceptionResolver для разрешения.
5.2 Подробное объяснение конфигурации DispathcherServlet
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- загрузка при запуске: указывает, что сервлет инициализируется при запуске контейнера;
- url-pattern: указывает, какие запросы обрабатываются Spring Web MVC, а «/» используется для определения сопоставления сервлета по умолчанию. так же может быть
*.html
Указывает на перехват всех запросов с расширением html - contextConfigLocation: представляет путь к файлу конфигурации SpringMVC.
Другая конфигурация параметров:
параметр | описывать |
---|---|
contextClass | Класс, реализующий интерфейс WebApplicationContext, который текущий сервлет использует для создания контекстов. Если этот параметр не указан, по умолчанию используется XmlWebApplicationContext. |
contextConfigLocation | Строка, передаваемая экземпляру контекста (указанному contextClass), указывающая расположение контекста. Эта строка может быть разделена на несколько строк (с использованием запятых в качестве разделителей) для поддержки нескольких контекстов (в случае нескольких контекстов, если один и тот же bean-компонент определен дважды, последний имеет приоритет). |
namespace | Пространство имен WebApplicationContext. Значение по умолчанию — [имя-сервера]-servlet. |
5.3 Конфигурация пружины
В предыдущем случае только SpringMVC, без Spring, веб-проект может быть запущен. В реальной разработке Spring и SpringMVC настраиваются отдельно, поэтому мы продолжаем улучшать вышеуказанный проект и добавлять конфигурацию, связанную со Spring.
Сначала добавьте в проект пакет службы и предоставьте класс HelloService следующим образом:
@Service
public class HelloService {
public String hello(String name) {
return "hello " + name;
}
}
Теперь предположим, что мне нужно внедрить HelloService в контейнер Spring и использовать его.Это bean-компонент, принадлежащий слою Spring, поэтому мы обычно регистрируем все bean-компоненты, кроме контроллера, в контейнере Spring, а контроллер регистрируем в контейнере SpringMVC. , добавьте applicationContext.xml в качестве конфигурации Spring в каталог ресурсов:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.javaboy" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
Однако этот файл конфигурации по умолчанию не загружается автоматически, поэтому нам нужно настроить его в web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Сначала укажите расположение файла конфигурации Spring через context-param.Для этого файла конфигурации также есть некоторые правила по умолчанию.Имя файла конфигурации по умолчанию называется applicationContext.xml, и если вы поместите этот файл конфигурации в каталог WEB-INF, то здесь не нужно указывать расположение конфигурационного файла, нужно только указать слушателя. Эта конфигурация является общей конфигурацией интегрированной веб-среды Spring и обычно используется для загрузки bean-компонентов, отличных от веб-уровня (например, DAO, Service и т. д.), для облегчения интеграции с любой другой веб-инфраструктурой.
- contextConfigLocation: указывает файл конфигурации, используемый для загрузки компонента;
- contextClass: указывает класс реализации ApplicationContext, используемый для загрузки bean-компонентов, WebApplicationContext по умолчанию.
После завершения настройки вам также необходимо изменить MyController и внедрить HelloSerivce в MyController:
@org.springframework.stereotype.Controller("/hello")
public class MyController implements Controller {
@Autowired
HelloService helloService;
/**
* 这就是一个请求处理接口
* @param req 这就是前端发送来的请求
* @param resp 这就是服务端给前端的响应
* @return 返回值是一个 ModelAndView,Model 相当于是我们的数据模型,View 是我们的视图
* @throws Exception
*/
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println(helloService.hello("javaboy"));
ModelAndView mv = new ModelAndView("hello");
mv.addObject("name", "javaboy");
return mv;
}
}
Уведомление
Чтобы сканировать MyController в контейнере SpringMVC, в MyController добавляется аннотация @Controller.В то же время, поскольку HandlerMapping, который мы сейчас используем, — это BeanNameUrlHandlerMapping (это означает, что адрес запроса — это имя bean-компонента-обработчика), нам также нужно чтобы вручную указать имя MyController.name.
Наконец, измените файл конфигурации SpringMVC, чтобы настроить bean-компонент для сканирования:
<context:component-scan base-package="org.javaboy.helloworld" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--这个是处理器映射器,这种方式,请求地址其实就是一个 Bean 的名字,然后根据这个 bean 的名字查找对应的处理器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" id="handlerMapping">
<property name="beanName" value="/hello"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" id="handlerAdapter"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
После завершения настройки снова запустите проект, и контейнер Spring также будет создан. Получите доступ к интерфейсу /hello, и метод hello в HelloService будет вызван автоматически.
5.4 Два контейнера
Когда Spring и SpringMVC появятся одновременно, в нашем проекте будет два контейнера, один контейнер Spring, а другой контейнер SpringMVC.Контейнер Spring загружается через ContextLoaderListener, а контейнер SpringMVC загружается через DispatcherServlet. два контейнера разные:
Как видно из рисунка:
- Контекстно-загруженные bean-компоненты, инициализированные ContextLoaderListener, совместно используются всем приложением, независимо от того, какая технология уровня представления используется, как правило, например, bean-компоненты уровня DAO и сервисного уровня;
- Bean-компоненты, загружаемые контекстом, инициализированным DispatcherServlet, — это bean-компоненты, которые допустимы только для Spring Web MVC, такие как Controller, HandlerMapping, HandlerAdapter и т. д. Контекст инициализации должен загружать только веб-компоненты.
- Почему бы не сканировать все bean-компоненты в контейнере Spring?
Это невозможно. Потому что после того, как запрос доходит до сервера, для его обработки используется DispatcherServlet, и он будет найден только в контейнере SpringMVC, а это значит, что Controller должен быть просканирован в контейнере SpringMVC.
2. Почему бы не сканировать все bean-компоненты в контейнере SpringMVC?
Это нормально, все bean-компоненты можно сканировать в контейнере SpringMVC. Есть две причины не писать их вместе:
- Для облегчения управления конфигурационными файлами
- В комбинации Spring+SpringMVC+Hibernate этот тип записи фактически не поддерживается.
6. Детали процессора
6.1 HandlerMapping
Обратите внимание, что упомянутый ниже процессор — это контроллер, который мы обычно видим
HandlerMapping, переведенный на китайский язык как обработчик отображения, в SpringMVC система предоставляет множество HandlerMappings:
HandlerMapping отвечает за поиск соответствующего процессора Handler и перехватчика Interceptor в соответствии с запросом запроса, инкапсуляцию их в объект HandlerExecutionChain и возврат во фронт-контроллер.
- BeanNameUrlHandlerMapping
Преобразователь процессора BeanNameUrl сопоставляет имя bean-компонента, определенного в контейнере Spring, в соответствии с запрошенным URL-адресом, чтобы найти экземпляр bean-компонента из контейнера Spring, то есть запрошенный адрес Url является именем bean-компонента процессора.
Это HandlerMapping настроено следующим образом:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" id="handlerMapping">
<property name="beanName" value="/hello"/>
</bean>
- SimpleUrlHandlerMapping
SimpleUrlHandlerMapping — это расширенная версия BeanNameUrlHandlerMapping, которая может единообразно отображать URL-адрес и идентификатор компонента-обработчика:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" id="handlerMapping">
<property name="mappings">
<props>
<prop key="/hello">myController</prop>
<prop key="/hello2">myController2</prop>
</props>
</property>
</bean>
Обратите внимание, что в свойствах вы можете настроить отношение сопоставления между несколькими путями запросов и экземплярами процессора.
6.2 HandlerAdapter
HandlerAdapter, переводится на китайский язык как адаптер процессора.
HandlerAdapter упаковывает (адаптирует) внутренний контроллер в соответствии с интерфейсом адаптера. После упаковки процессор может быть выполнен. Путем расширения адаптера процессора можно выполнять различные типы процессоров. Здесь используется шаблон проектирования адаптера.
В SpringMVC HandlerAdapter также имеет множество классов реализации:
- SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter Простой адаптер обработчика контроллера, все bean-компоненты, реализующие интерфейс org.springframework.web.servlet.mvc.Controller, адаптируются и выполняются через этот адаптер, то есть, если разрабатываемый нами интерфейс выполняется путем реализации интерфейса Controller (а не интерфейса разработан с помощью аннотаций), тогда HandlerAdapter должен быть SimpleControllerHandlerAdapter.
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
- HttpRequestHandlerAdapter
HttpRequestHandlerAdapter, адаптер обработчика http-запросов, все bean-компоненты, реализующие интерфейс org.springframework.web.HttpRequestHandler, адаптируются и выполняются через этот адаптер.
Например, существует следующий интерфейс:
@Controller
public class MyController2 implements HttpRequestHandler {
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("-----MyController2-----");
}
}
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" id="handlerMapping">
<property name="mappings">
<props>
<prop key="/hello2">myController2</prop>
</props>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" id="handlerAdapter"/>
6.3 Передовой опыт
Всевозможные ситуации в целом понятны, давайте посмотрим на конкретную практику в проекте.
- Автоматическое сканирование компонентов
В веб-разработке мы в основном больше не создаем экземпляр Bean через конфигурацию XML или Java, а напрямую реализуем конфигурацию Bean через сканирование компонентов.Если вы хотите сканировать несколько пакетов, разделите их с помощью Can:
<context:component-scan base-package="org.sang"/>
- HandlerMapping
Обычно в нашем проекте мы используем RequestMappingHandlerMapping, который сопоставляет запросы в соответствии с аннотациями в процессоре (то есть атрибутом url в аннотации @RequestMapping). Поскольку все мы разрабатываем интерфейсы, реализуя описанные выше классы, что эквивалентно одному классу и одному интерфейсу, мы можем использовать RequestMappingHandlerMapping в качестве обработчика отображения, чтобы мы могли разрабатывать несколько интерфейсов в одном классе.
- HandlerAdapter
Для вышеупомянутых методов интерфейса, определенных аннотацией @RequestMapping, вызов этих методов реализован через адаптер RequestMappingHandlerAdapter.
Например, мы разрабатываем интерфейс:
@Controller
public class MyController3 {
@RequestMapping("/hello3")
public ModelAndView hello() {
return new ModelAndView("hello3");
}
}
Чтобы получить доступ к этому интерфейсу, нам нужен RequestMappingHandlerMapping, чтобы найти метод, который нужно выполнить, и RequestMappingHandlerAdapter, чтобы выполнить обнаруженный метод.Измените файл конфигурации springmvc следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.javaboy.helloworld"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" id="handlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" id="handlerAdapter"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Затем запустите проект, войдите в интерфейс /hello3, и вы увидите соответствующую страницу.
- продолжать оптимизировать
Поскольку в разработке мы обычно используем RequestMappingHandlerMapping и RequestMappingHandlerAdapter, эти два имеют упрощенный способ написания, как показано ниже:
<mvc:annotation-driven>
Эту строку конфигурации можно использовать вместо двух строк конфигурации для RequestMappingHandlerMapping и RequestMappingHandlerAdapter.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="org.javaboy.helloworld"/>
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Эффект доступа такой же, как и на предыдущем шаге. Это последняя конфигурация в нашей актуальной разработке.
7.1 @RequestMapping
Эта аннотация используется для обозначения интерфейса, что является одной из наиболее часто используемых аннотаций при разработке интерфейса.
7.1.1 URL-адрес запроса
Пометить URL-адрес запроса так же просто, как добавить эту аннотацию к соответствующему методу:
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView hello() {
return new ModelAndView("hello");
}
}
Здесь @RequestMapping("/hello") означает, что этот метод будет запущен, когда адрес запроса будет /hello. Среди них адрес может быть множественным, то есть несколько адресов могут быть сопоставлены одному и тому же методу.
@Controller
public class HelloController {
@RequestMapping({"/hello","/hello2"})
public ModelAndView hello() {
return new ModelAndView("hello");
}
}
Эта конфигурация означает, что и /hello, и /hello2 могут получить доступ к методу.
7.1.2 Сужение запроса
В одном и том же проекте будет несколько интерфейсов, например, интерфейсы, связанные с заказом, имеют формат /order/xxx, а интерфейсы, связанные с пользователем, — в формате /user/xxx. Для удобства обработки префиксы здесь (то есть /order, /user) могут быть единообразно обработаны на контроллере.
@Controller
@RequestMapping("/user")
public class HelloController {
@RequestMapping({"/hello","/hello2"})
public ModelAndView hello() {
return new ModelAndView("hello");
}
}
Когда в класс добавляется аннотация @RequestMapping, в это время для доступа к приветствию адрес должен быть/user/hello
или/user/hello2
7.1.3 Ограничения метода запроса
По умолчанию к методам, определенным с помощью аннотации @RequestMapping, можно обращаться с помощью запросов GET и POST, но запросы DELETE и PUT недоступны.
Конечно, мы также можем указать конкретные методы доступа:
@Controller
@RequestMapping("/user")
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public ModelAndView hello() {
return new ModelAndView("hello");
}
}
Через аннотацию @RequestMapping указано, что доступ к интерфейсу возможен только с помощью GET-запросов, в настоящее время доступ к интерфейсу с помощью POST-запросов и запросов-запросов невозможен. Принудительный доступ сообщит о следующей ошибке:
Конечно, может быть более одного ограниченного метода:
@Controller
@RequestMapping("/user")
public class HelloController {
@RequestMapping(value = "/hello",method = {RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
public ModelAndView hello() {
return new ModelAndView("hello");
}
}
На этом этапе к интерфейсу можно получить доступ с помощью GET, POST, PUT и DELETE. Однако, поскольку JSP поддерживает GET, POST и HEAD, этот тест не может использовать JSP в качестве шаблона страницы. Вы можете изменить вид на что-то другое или вернуть JSON, но здесь это не имеет значения.
7.2 Возвращаемое значение метода Controller
7.2.1 Возврат к ModelAndView
Если это разработка фронтенда и бэкенда, то в большинстве случаев возвращаем ModelAndView, то есть модель данных + представление:
@Controller
@RequestMapping("/user")
public class HelloController {
@RequestMapping("/hello")
public ModelAndView hello() {
ModelAndView mv = new ModelAndView("hello");
mv.addObject("username", "javaboy");
return mv;
}
}
Модель, поместите наши данные, а затем укажите имя представления в ModelAndView.
7.2.2 Возвращение в пустоту
Нет возвращаемого значения. Возвращаемого значения нет, не обязательно, что возвращаемого значения нет, но возвращаемое значение метода недействительно, и мы можем вернуться к внешнему интерфейсу другими способами.По сути, этот метод также можно понимать как набор схем в сервлете.
Обратите внимание, что, поскольку проект Maven по умолчанию не имеет сервлета, здесь необходимо добавить дополнительную зависимость:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
- Прыжок на стороне сервера через HttpServletRequest
@RequestMapping("/hello2")
public void hello2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/jsp/hello.jsp").forward(req,resp);//服务器端跳转
}
- Перенаправление через HttpServletResponse
@RequestMapping("/hello3")
public void hello3(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.sendRedirect("/hello.jsp");
}
Вы также можете вручную указать заголовок ответа для перенаправления:
@RequestMapping("/hello3")
public void hello3(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(302);
resp.addHeader("Location", "/jsp/hello.jsp");
}
- Дайте ответ через HttpServletResponse
@RequestMapping("/hello4")
public void hello4(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("hello javaboy!");
out.flush();
out.close();
}
Таким образом, могут быть возвращены как JSON, так и обычные строки.
7.2.3 Возврат строки
- Возвращает имя логического представления
Предыдущий ModelAndView можно разделить на две части: Model и View, в SpringMVC Model можно указать прямо в параметрах, и тогда возвращаемым значением будет имя логического представления:
@RequestMapping("/hello5")
public String hello5(Model model) {
model.addAttribute("username", "javaboy");//这是数据模型
return "hello";//表示去查找一个名为 hello 的视图
}
- прыжок сервера
@RequestMapping("/hello5")
public String hello5() {
return "forward:/jsp/hello.jsp";
}
вперед следует путь для перехода.
- Прыжок клиента
@RequestMapping("/hello5")
public String hello5() {
return "redirect:/user/hello";
}
По сути, это перенаправление браузера.
- действительно возвращает строку
Приведенные выше три возвращаемые строки имеют особое значение. Если вы должны вернуть строку, вам нужно добавить дополнительное примечание: @ResponseBody , эта аннотация указывает, что возвращаемое значение текущего метода должно отображать возвращаемое значение и не имеет специального значения. смысл. .
@RequestMapping("/hello5")
@ResponseBody
public String hello5() {
return "redirect:/user/hello";
}
Приведенный выше код означает, что вы хотите вернуть часть контента какredirect:/user/hello
строка, он не имеет особого значения. Учтите, что если вы просто вернете строку на китайском языке, она будет искажена. Чтобы решить эту проблему, вы можете добавить атрибут products к @RequestMapping:
@RequestMapping(value = "/hello5",produces = "text/html;charset=utf-8")
@ResponseBody
public String hello5() {
return "Java 语言程序设计";
}
7.3 Привязка параметров
7.3.1 Типы параметров, поддерживаемые по умолчанию
Типы параметров, поддерживаемые по умолчанию, — это типы параметров, которые могут быть непосредственно записаны в методах, аннотированных @RequestMapping.Существует четыре типа:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- Model/ModelMap
Эти примеры могут относиться к предыдущему разделу.
В запрашиваемом методе это параметры по умолчанию, если эти параметры просто необходимы в методе, то эти параметры можно добавить в метод.
7.3.2 Простые типы данных
Также поддерживаются простые типы данных, такие как Integer, Boolean, Double и т. д. Например, чтобы добавить книгу:
Сначала создайте add book.jsp в каталоге /jsp/ в качестве страницы добавления книги:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/doAdd" method="post">
<table>
<tr>
<td>书名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>作者:</td>
<td><input type="text" name="author"></td>
</tr>
<tr>
<td>价格:</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>是否上架:</td>
<td>
<input type="radio" value="true" name="ispublic">是
<input type="radio" value="false" name="ispublic">否
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="添加">
</td>
</tr>
</table>
</form>
</body>
</html>
Создайте контроллер, контроллер предоставляет две функции: одна для доступа к странице jsp, а другая для предоставления интерфейса добавления:
@Controller
public class BookController {
@RequestMapping("/book")
public String addBook() {
return "addbook";
}
@RequestMapping(value = "/doAdd",method = RequestMethod.POST)
@ResponseBody
public void doAdd(String name,String author,Double price,Boolean ispublic) {
System.out.println(name);
System.out.println(author);
System.out.println(price);
System.out.println(ispublic);
}
}
Обратите внимание: поскольку метод doAdd на самом деле не хочет возвращать какое-либо значение, вам нужно добавить к методу аннотацию @ResponseBody, указывающую, что метод заканчивается здесь, и нет необходимости искать соответствующее представление. Кроме того, китайцы, отправляемые POST-запросом, будут искажены, поэтому добавим в web.xml дополнительный фильтр кодировки:
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Наконец, введите в браузереhttp://localhost:8080/book, вы можете выполнить операцию добавления, и сервер распечатает соответствующий журнал.
В приведенной выше привязке есть требование, чтобы атрибут имени поля в форме совпадал один к одному с именем переменной в интерфейсе, чтобы сопоставление могло быть успешным, иначе сервер не может получить данные от передний конец. В некоторых особых случаях имя переменной интерфейса нашего сервера может не совпадать с внешним интерфейсом.В настоящее время мы можем использовать аннотацию @RequestParam для решения этой проблемы.
- @RequestParam
У этой аннотации три основные функции:
- псевдоним переменной
- Требуется ли переменная настройки
- Установить значения по умолчанию для переменных
следующее:
@RequestMapping(value = "/doAdd",method = RequestMethod.POST)
@ResponseBody
public void doAdd(@RequestParam("name") String bookname, String author, Double price, Boolean ispublic) {
System.out.println(bookname);
System.out.println(author);
System.out.println(price);
System.out.println(ispublic);
}
«Имя» в аннотации представляет собой псевдоним для переменной bookname, то есть bookname получит значение имени переменной из внешнего интерфейса. В этой аннотации вы также можете добавить обязательный атрибут и атрибут defaultValue следующим образом:
@RequestMapping(value = "/doAdd",method = RequestMethod.POST)
@ResponseBody
public void doAdd(@RequestParam(value = "name",required = true,defaultValue = "三国演义") String bookname, String author, Double price, Boolean ispublic) {
System.out.println(bookname);
System.out.println(author);
System.out.println(price);
System.out.println(ispublic);
}
Требуемый атрибут по умолчанию имеет значение true, то есть, пока добавлена аннотация @RequestParam, этот параметр является обязательным по умолчанию, если он не заполнен, запрос не может быть отправлен, и будет сообщено об ошибке 400. Если это параметр необязателен, вы можете вручную установить для требуемого атрибута значение false. Однако, если при этом установлено defaultValue, в это время интерфейс не передает этот параметр на сервер, даже если требуемый атрибут равен true, он не сообщит об ошибке.
7.3.3 Классы сущностей
Помимо простых типов данных, параметры также могут быть классами сущностей. На самом деле в разработке в большинстве случаев это класс сущностей.
По-прежнему в приведенном выше примере мы используем объект Book вместо того, чтобы получать данные из внешнего интерфейса:
public class Book {
private String name;
private String author;
private Double price;
private Boolean ispublic;
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", ispublic=" + ispublic +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Boolean getIspublic() {
return ispublic;
}
public void setIspublic(Boolean ispublic) {
this.ispublic = ispublic;
}
}
Сервер получает данные следующим образом:
@RequestMapping(value = "/doAdd",method = RequestMethod.POST)
@ResponseBody
public void doAdd(Book book) {
System.out.println(book);
}
При передаче значения на странице внешнего интерфейса это то же самое, что и выше, вам нужно только написать имя атрибута, и вам не нужно писать имя объекта книги.
Конечно, могут быть объекты внутри объектов. Например, следующие объекты:
public class Book {
private String name;
private Double price;
private Boolean ispublic;
private Author author;
public void setAuthor(Author author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
", ispublic=" + ispublic +
", author=" + author +
'}';
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Boolean getIspublic() {
return ispublic;
}
public void setIspublic(Boolean ispublic) {
this.ispublic = ispublic;
}
}
public class Author {
private String name;
private Integer age;
@Override
public String toString() {
return "Author{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
В объекте Книга есть атрибут Автор Как передать значение атрибуту Автор? Передняя часть написана следующим образом:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/doAdd" method="post">
<table>
<tr>
<td>书名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>作者姓名:</td>
<td><input type="text" name="author.name"></td>
</tr>
<tr>
<td>作者年龄:</td>
<td><input type="text" name="author.age"></td>
</tr>
<tr>
<td>价格:</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>是否上架:</td>
<td>
<input type="radio" value="true" name="ispublic">是
<input type="radio" value="false" name="ispublic">否
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="添加">
</td>
</tr>
</table>
</form>
</body>
</html>
Таким образом, все данные могут быть получены непосредственно объектом Book в бэкенде.
7.3.4 Привязка пользовательских параметров
Предыдущие преобразования автоматически преобразуются системой, и это преобразование ограничено базовыми типами данных. Специальные типы данных, которые система не может преобразовать автоматически, например даты. Например, интерфейс передает дату серверу, а сервер использует для ее получения не строку, а объект Date.В это время происходит сбой преобразования типа параметра. В настоящее время нам нужно вручную определить преобразователь типа параметра, чтобы вручную преобразовать строку даты в объект Date.
@Component
public class DateConverter implements Converter<String, Date> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public Date convert(String source) {
try {
return sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
В преобразователе типов настраиваемых параметров преобразуйте String в объект Date и в то же время зарегистрируйте преобразователь как Bean.
Затем в файле конфигурации SpringMVC настройте bean-компонент, чтобы он вступил в силу.
<mvc:annotation-driven conversion-service="conversionService"/>
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
<property name="converters">
<set>
<ref bean="dateConverter"/>
</set>
</property>
</bean>
После завершения настройки сервер может получить параметры даты от внешнего интерфейса.
7.3.5 Параметры классов коллекций
- Строковый массив
Строковые массивы могут приниматься массивами напрямую, при передаче фронтенда массивы фактически передаются с тем же ключом, который обычно используется в чекбоксах.
Например, интерфейс добавляет элемент хобби:
<form action="/doAdd" method="post">
<table>
<tr>
<td>书名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>作者姓名:</td>
<td><input type="text" name="author.name"></td>
</tr>
<tr>
<td>作者年龄:</td>
<td><input type="text" name="author.age"></td>
</tr>
<tr>
<td>出生日期:</td>
<td><input type="date" name="author.birthday"></td>
</tr>
<tr>
<td>兴趣爱好:</td>
<td>
<input type="checkbox" name="favorites" value="足球">足球
<input type="checkbox" name="favorites" value="篮球">篮球
<input type="checkbox" name="favorites" value="乒乓球">乒乓球
</td>
</tr>
<tr>
<td>价格:</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>是否上架:</td>
<td>
<input type="radio" value="true" name="ispublic">是
<input type="radio" value="false" name="ispublic">否
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="添加">
</td>
</tr>
</table>
</form>
Используйте массив на сервере для получения избранных объектов:
@RequestMapping(value = "/doAdd",method = RequestMethod.POST)
@ResponseBody
public void doAdd(Book book,String[] favorites) {
System.out.println(Arrays.toString(favorites));
System.out.println(book);
}
Обратите внимание, что серверная сторона не может использовать коллекцию List для получения объектов массива, отправленных из внешнего интерфейса.
- Коллекция списков
Если вам нужно использовать коллекцию List для получения данных из внешнего интерфейса, то саму коллекцию List необходимо поместить в инкапсулированный объект.На данный момент List может быть базовым типом данных или объектом. Например, есть классный класс, в классе есть ученики и несколько учеников:
public class MyClass {
private Integer id;
private List<Student> students;
@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", students=" + students +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
public class Student {
private Integer id;
private String name;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
При добавлении класса вы можете передать несколько Студентов.Внешняя страница написана следующим образом:
<form action="/addclass" method="post">
<table>
<tr>
<td>班级编号:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>学生编号:</td>
<td><input type="text" name="students[0].id"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="students[0].name"></td>
</tr>
<tr>
<td>学生编号:</td>
<td><input type="text" name="students[1].id"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="students[1].name"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
Сервер может напрямую получать данные:
@RequestMapping("/addclass")
@ResponseBody
public void addClass(MyClass myClass) {
System.out.println(myClass);
}
- Map
По сравнению с классами сущностей Map является более гибким решением, однако Map менее удобен в сопровождении, поэтому его обычно не рекомендуют.
Например, добавьте другую информацию об атрибутах в указанный выше класс класса:
public class MyClass {
private Integer id;
private List<Student> students;
private Map<String, Object> info;
@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", students=" + students +
", info=" + info +
'}';
}
public Map<String, Object> getInfo() {
return info;
}
public void setInfo(Map<String, Object> info) {
this.info = info;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
Во внешнем интерфейсе присвойте значение информационной карте следующим образом.
<form action="/addclass" method="post">
<table>
<tr>
<td>班级编号:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>班级名称:</td>
<td><input type="text" name="info['name']"></td>
</tr>
<tr>
<td>班级位置:</td>
<td><input type="text" name="info['pos']"></td>
</tr>
<tr>
<td>学生编号:</td>
<td><input type="text" name="students[0].id"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="students[0].name"></td>
</tr>
<tr>
<td>学生编号:</td>
<td><input type="text" name="students[1].id"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="students[1].name"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
8. Загрузка файла
Загрузка файлов инкапсулирована в SpringMVC, и мы можем реализовать загрузку файлов более удобно. Начиная с Spring 3.1 для загрузки файлов предусмотрено два обработчика:
- CommonsMultipartResolver
- StandardServletMultipartResolver
Первый процессор имеет хорошую совместимость и совместим с версиями до Servlet 3.0, но он полагается на сторонний инструмент commons-fileupload, поэтому, если вы используете его, вы должны добавить зависимость commons-fileupload.
У второго процессора плохая совместимость.Подходит для версий после Servlet 3.0.Не зависит от сторонних инструментов.С его помощью можно напрямую загружать файлы.
8.1 CommonsMultipartResolver
Чтобы использовать CommonsMultipartResolver для загрузки файлов, вам необходимо сначала добавить зависимость commons-fileupload следующим образом:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
Затем в файле конфигурации SpringMVC настройте MultipartResolver:
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
Обратите внимание, что этот bean-компонент должен иметь идентификатор, а идентификатор должен быть multipartResolver.
Затем создайте страницу jsp:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
Обратите внимание, что запрос на загрузку файла является запросом POST, а enctype должен быть multipart/form-data.
Затем разработайте интерфейс загрузки файлов:
@Controller
public class FileUploadController {
SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");
@RequestMapping("/upload")
@ResponseBody
public String upload(MultipartFile file, HttpServletRequest req) {
String format = sdf.format(new Date());
String realPath = req.getServletContext().getRealPath("/img") + format;
File folder = new File(realPath);
if (!folder.exists()) {
folder.mkdirs();
}
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
try {
file.transferTo(new File(folder, newName));
String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName;
return url;
} catch (IOException e) {
e.printStackTrace();
}
return "failed";
}
}
В этом методе загрузки файла делается всего четыре вещи:
- Решите путь сохранения файла, вот каталог img, сохраненный в рабочем каталоге проекта, а затем используйте дату, чтобы продолжить классификацию.
- Чтобы решить проблему с именем файла, используйте UUID в качестве нового имени файла, чтобы заменить старое имя файла, что может эффективно предотвратить конфликты имен файлов.
- сохранить документ
- Создать путь доступа к файлу
Здесь также есть небольшая проблема, в SpringMVC статические ресурсы по умолчанию автоматически перехватываются и не могут быть доступны, а значит, успешно загруженные изображения не могут быть доступны, поэтому нам также нужно добавить следующую конфигурацию в файл конфигурации SpringMVC:
<mvc:resources mapping="/**" location="/"/>
После этого вы можете посетить страницу jsp и загрузить файл.
Конечно, конфигурация по умолчанию не обязательно соответствует нашим потребностям, мы также можем вручную настроить размер загружаемого файла и т. д.:
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--默认的编码-->
<property name="defaultEncoding" value="UTF-8"/>
<!--上传的总文件大小-->
<property name="maxUploadSize" value="1048576"/>
<!--上传的单个文件大小-->
<property name="maxUploadSizePerFile" value="1048576"/>
<!--内存中最大的数据量,超过这个数据量,数据就要开始往硬盘中写了-->
<property name="maxInMemorySize" value="4096"/>
<!--临时目录,超过 maxInMemorySize 配置的大小后,数据开始往临时目录写,等全部上传完成后,再将数据合并到正式的文件上传目录-->
<property name="uploadTempDir" value="file:///E:\\tmp"/>
</bean>
8.2 StandardServletMultipartResolver
Этот метод загрузки файлов не должен полагаться на сторонние jar-файлы (в основном потому, что нет необходимости добавлять зависимость commons-fileupload), но он не поддерживает версии до Servlet 3.0.
Используя StandardServletMultipartResolver, мы сначала настраиваем этот bean-компонент в файле конфигурации SpringMVC:
<bean class="org.springframework.web.multipart.support.StandardServletMultipartResolver" id="multipartResolver">
</bean>
Обратите внимание, что имя компонента здесь по-прежнему называется multipartResolver.
После завершения настройки обратите внимание, что этот компонент не может напрямую настраивать ограничения, такие как размер загружаемого файла. Его нужно настроить в web.xml (здесь, даже если вам не нужно ограничивать размер загружаемого файла, вам нужно настроить multipart-config в web.xml):
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<multipart-config>
<!--文件保存的临时目录,这个目录系统不会主动创建-->
<location>E:\\temp</location>
<!--上传的单个文件大小-->
<max-file-size>1048576</max-file-size>
<!--上传的总文件大小-->
<max-request-size>1048576</max-request-size>
<!--这个就是内存中保存的文件最大大小-->
<file-size-threshold>4096</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
После завершения настройки вы можете протестировать загрузку файла, метод проверки такой же, как описано выше.
8.3 Загрузка нескольких файлов
Существует два типа загрузки нескольких файлов: один для файлов с одним и тем же ключом, а другой — для файлов с разными ключами.
8.3.1 файлы с одинаковым ключом
Для этого типа загрузки страница интерфейса обычно выглядит следующим образом:
<form action="/upload2" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple>
<input type="submit" value="上传">
</form>
Основная причина в том, что во входном узле есть несколько атрибутов. Бэкэнд использует массив для получения файлов:
@RequestMapping("/upload2")
@ResponseBody
public void upload2(MultipartFile[] files, HttpServletRequest req) {
String format = sdf.format(new Date());
String realPath = req.getServletContext().getRealPath("/img") + format;
File folder = new File(realPath);
if (!folder.exists()) {
folder.mkdirs();
}
try {
for (MultipartFile file : files) {
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
file.transferTo(new File(folder, newName));
String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName;
System.out.println(url);
}
} catch (IOException e) {
e.printStackTrace();
}
}
8.3.2 Различные файлы для ключа
Ключ отличается, общее определение интерфейса выглядит следующим образом:
<form action="/upload3" method="post" enctype="multipart/form-data">
<input type="file" name="file1">
<input type="file" name="file2">
<input type="submit" value="上传">
</form>
В этом случае вы можете использовать разные переменные для его получения в бэкенде:
@RequestMapping("/upload3")
@ResponseBody
public void upload3(MultipartFile file1, MultipartFile file2, HttpServletRequest req) {
String format = sdf.format(new Date());
String realPath = req.getServletContext().getRealPath("/img") + format;
File folder = new File(realPath);
if (!folder.exists()) {
folder.mkdirs();
}
try {
String oldName = file1.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
file1.transferTo(new File(folder, newName));
String url1 = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName;
System.out.println(url1);
String oldName2 = file2.getOriginalFilename();
String newName2 = UUID.randomUUID().toString() + oldName2.substring(oldName2.lastIndexOf("."));
file2.transferTo(new File(folder, newName2));
String url2 = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName2;
System.out.println(url2);
} catch (IOException e) {
e.printStackTrace();
}
}
9. Глобальная обработка исключений
В проекте может быть выброшено несколько исключений, и мы не можем напрямую отобразить информацию о стеке исключения пользователю по двум причинам:
- Плохой пользовательский опыт
- очень небезопасно
Поэтому для исключений мы можем настроить обработку исключений.В SpringMVC предусмотрены соответствующие решения и для глобальных исключений, в основном через аннотации @ControllerAdvice и @ExceptionHandler.
Взяв в качестве примера размер загружаемого файла, превышающий ограничение в Разделе 8, для настройки исключения вам нужно только предоставить класс обработки исключений:
@ControllerAdvice//表示这是一个增强版的 Controller,主要用来做全局数据处理
public class MyException {
@ExceptionHandler(Exception.class)
public ModelAndView fileuploadException(Exception e) {
ModelAndView error = new ModelAndView("error");
error.addObject("error", e.getMessage());
return error;
}
}
это здесь:
- @ControllerAdvice указывает, что это расширенная версия контроллера, которая в основном используется для глобальной обработки данных.
- @ExceptionHandler указывает, что это метод обработки исключений. Параметр этой аннотации указывает на исключение, которое необходимо перехватить. Параметр Exception указывает, что перехватываются все исключения. Он также может быть специфичным для определенного исключения. Если он специфичен для определенное исключение, другие исключения не будут заблокированы.
- Определение метода исключения, как и определение метода в контроллере, может возвращать ModelAndview, возвращать String или void.
Например, следующие команды кода перехватывают исключения загрузки файлов. Другие исключения не имеют к этому никакого отношения и не войдут в пользовательский метод обработки исключений.
@ControllerAdvice//表示这是一个增强版的 Controller,主要用来做全局数据处理
public class MyException {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView fileuploadException(MaxUploadSizeExceededException e) {
ModelAndView error = new ModelAndView("error");
error.addObject("error", e.getMessage());
return error;
}
}
10. Проверка данных сервера
В системе B/S проверка данных HTTP-запроса в основном выполняется на стороне клиента, что также необходимо для простоты и удобства пользователя, однако в некоторых системах с высокими требованиями к безопасности проверка на стороне сервера незаменима. почти все системы, когда дело доходит до проверки данных, требуют вторичной проверки на стороне сервера. Зачем нам нужно выполнять вторичную проверку на стороне сервера? Это требует понимания цели проверки на стороне клиента и проверки на стороне сервера.
- Для проверки на стороне клиента мы в основном стремимся улучшить взаимодействие с пользователем. Например, если пользователь вводит адрес электронной почты, чтобы проверить, является ли адрес электронной почты законным, нет необходимости отправлять его на сервер для проверки. проверяется непосредственно с помощью js на внешнем интерфейсе. Но каждый должен понимать, что внешняя проверка не может заменить внутреннюю проверку.Внешняя проверка может эффективно улучшить взаимодействие с пользователем, но не может гарантировать целостность данных, поскольку в архитектуре B/S пользователи могут легко получить адрес запроса. . , а затем отправить запрос напрямую, передав недопустимые параметры.
- Проверка на стороне сервера, хотя пользовательский опыт не очень хорош, может эффективно обеспечить безопасность и целостность данных.
- Подводя итог, в реальном проекте они используются вместе.
Spring поддерживает структуру проверки JSR-303.JSR-303 — это подспецификация в JAVA EE 6, называемая «Проверка компонентов».Официальной эталонной реализацией является Hibernate Validator (не связанный с Hibernate ORM).Значения полей проверяются.
10.1 Обычная проверка
Обычная верификация — это самое основное использование здесь.
Во-первых, нам нужно добавить зависимости, необходимые для проверки:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
Затем настройте bean-компонент проверки в файле конфигурации SpringMVC:
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" id="validatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<mvc:annotation-driven validator="validatorFactoryBean"/>
При настройке предоставьте экземпляр LocalValidatorFactoryBean, а затем используйте HibernateValidator для проверки компонента.
На этом настройка завершена.
Далее мы предоставляем страницу для добавления студентов:
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
Среди данных, которые должны быть представлены здесь, предполагается, что номер студента не может быть пустым, имя студента не может превышать 10 и не может быть пустым, адрес электронной почты должен быть законным, а возраст не может превышать 150. Затем при определении класса сущности вы можете добавить это условие оценки.
public class Student {
@NotNull
private Integer id;
@NotNull
@Size(min = 2,max = 10)
private String name;
@Email
private String email;
@Max(150)
private Integer age;
public String getEmail() {
return email;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
это здесь:
- @NotNull означает, что это поле не может быть пустым
- Ограничение на длину этой строки описано в @Size
- @Email указывает, что значение этого поля должно быть адресом электронной почты.
- @Max указывает максимальное значение этого поля
После того, как определение завершено, затем определите интерфейс в контроллере:
@Controller
public class StudentController {
@RequestMapping("/addstudent")
@ResponseBody
public void addStudent(@Validated Student student, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName()+":"+allError.getDefaultMessage());
}
}
}
}
это здесь:
- @Validated означает, что правила проверки, определенные в Student, вступят в силу.
- BindingResult указывает информацию об ошибке.Если эта переменная не пуста, это указывает на наличие ошибки, в противном случае проверка пройдена.
Затем вы можете начать проект. Посетите страницу jsp, а затем добавьте Student, чтобы проверить, вступили ли в силу правила проверки.
По умолчанию печатаемое сообщение об ошибке является системным сообщением об ошибке по умолчанию. Мы также можем настроить это сообщение об ошибке. Настройте следующим образом:
Так как китайский язык в файле свойств будет искажен, нам нужно сначала изменить конфигурацию IDEA, щелкнув Файл-->Настройки->Редактор-->Кодировки файлов, следующим образом:
Затем определите текст сообщения об ошибке и создайте новый файл MyMessage.properties в каталоге ресурсов со следующим содержимым:
student.id.notnull=id 不能为空
student.name.notnull=name 不能为空
student.name.length=name 最小长度为 2 ,最大长度为 10
student.email.error=email 地址非法
student.age.error=年龄不能超过 150
Затем в конфигурации SpringMVC загрузите этот файл конфигурации:
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" id="validatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="bundleMessageSource"/>
</bean>
<bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="bundleMessageSource">
<property name="basenames">
<list>
<value>classpath:MyMessage</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="300"/>
</bean>
<mvc:annotation-driven validator="validatorFactoryBean"/>
Наконец, в аннотации к классу сущности добавьте информацию о возникновении ошибки проверки:
public class Student {
@NotNull(message = "{student.id.notnull}")
private Integer id;
@NotNull(message = "{student.name.notnull}")
@Size(min = 2,max = 10,message = "{student.name.length}")
private String name;
@Email(message = "{student.email.error}")
private String email;
@Max(value = 150,message = "{student.age.error}")
private Integer age;
public String getEmail() {
return email;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
После завершения настройки, если при проверке возникнет еще одна ошибка, будет отображаться наше собственное сообщение об ошибке.
10.2 Групповая проверка
Поскольку правила проверки определены в классе сущностей, правила проверки могут различаться в разных средах отправки данных. Например, id пользователя самовозрастающий, при добавлении не нужно передавать id пользователя, а при изменении надо передавать id пользователя, в этом случае нужно использовать групповую верификацию.
Для групповой верификации сначала нужно определить группу верификации, так называемая группа верификации — это фактически пустой интерфейс:
public interface ValidationGroup1 {
}
public interface ValidationGroup2 {
}
Затем в классе сущностей укажите группу, к которой принадлежит каждое правило проверки:
public class Student {
@NotNull(message = "{student.id.notnull}",groups = ValidationGroup1.class)
private Integer id;
@NotNull(message = "{student.name.notnull}",groups = {ValidationGroup1.class, ValidationGroup2.class})
@Size(min = 2,max = 10,message = "{student.name.length}",groups = {ValidationGroup1.class, ValidationGroup2.class})
private String name;
@Email(message = "{student.email.error}",groups = {ValidationGroup1.class, ValidationGroup2.class})
private String email;
@Max(value = 150,message = "{student.age.error}",groups = {ValidationGroup2.class})
private Integer age;
public String getEmail() {
return email;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
В группе укажите группу, к которой принадлежит каждое правило проверки.Правило может принадлежать одной группе или нескольким группам.
Наконец, где параметры получены, укажите группу проверки:
@Controller
public class StudentController {
@RequestMapping("/addstudent")
@ResponseBody
public void addStudent(@Validated(ValidationGroup2.class) Student student, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName()+":"+allError.getDefaultMessage());
}
}
}
}
После завершения настройки вступят в силу правила проверки, принадлежащие группе ValidationGroup2.
10.3 Аннотации проверки
Аннотации проверки в основном следующие:
- @Null аннотированный элемент должен быть нулевым
- @NotNull аннотированный элемент не должен быть нулевым
- @AssertTrue аннотированный элемент должен быть истинным
- @AssertFalse аннотированный элемент должен быть ложным
- @Min(value) Аннотируемый элемент должен быть числом, а его значение должно быть больше или равно указанному минимальному значению.
- @Max(value) Аннотируемый элемент должен быть числом, а его значение должно быть меньше или равно указанному максимальному значению.
- @DecimalMin(value) Аннотируемый элемент должен быть числом, а его значение должно быть больше или равно указанному минимальному значению.
- @DecimalMax(значение) Аннотируемый элемент должен быть числом, а его значение должно быть меньше или равно указанному максимальному значению.
- @Size(max=, min=) Размер аннотируемого элемента должен быть в пределах указанного диапазона
- @Digits (целое число, дробь) аннотируемый элемент должен быть числом, и его значение должно быть в допустимом диапазоне
- Аннотированный элемент @Past должен быть датой в прошлом.
- Аннотированный элемент @Future должен быть датой в будущем.
- @Pattern(regex=,flag=) Аннотированный элемент должен соответствовать указанному регулярному выражению.
- @NotBlank(message =) Убедитесь, что строка не является нулевой и ее длина должна быть больше 0
- @Email аннотированный элемент должен быть адресом электронной почты
- @Length(min=,max=) Размер аннотированной строки должен быть в пределах указанного диапазона
- @NotEmpty аннотированная строка должна быть непустой
- @Range(min=,max=,message=) Аннотируемый элемент должен находиться в соответствующем диапазоне
11.1 Основное использование эха данных
Эхо данных предназначено для автоматического заполнения введенных данных в случае сбоя отправки пользовательских данных. Вообще говоря, если для отправки данных используется Ajax, в принципе нет необходимости в отображении данных, но если данные отправляются через формы, то отображение данных очень необходимо.
11.1.1 Простые типы данных
Простые типы данных, на самом деле фреймворк не предоставляет здесь никакой поддержки, то есть мы настраиваем это вручную. Мы продолжаем демонстрировать Демо на примере в Разделе 10. Если отправленные данные о студенте не соответствуют требованиям, то вернитесь на страницу «Добавить студента» и предварительно заполните ранее заполненные данные.
Во-первых, давайте изменим страницу student.jsp:
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
При получении данных используйте простые типы данных для получения:
@RequestMapping("/addstudent")
public String addStudent2(Integer id, String name, String email, Integer age, Model model) {
model.addAttribute("id", id);
model.addAttribute("name", name);
model.addAttribute("email", email);
model.addAttribute("age", age);
return "student";
}
Таким образом, это эквивалентно тому, что фреймворк не выполняет никакой работы, то есть мы выполняем эхо данных вручную. В это время, если к странице осуществляется доступ, сервер снова найдет страницу, и данные будут предварительно заполнены.
11.1.2 Классы сущностей
Эхо приведенного выше простого типа данных на самом деле очень хлопотно, потому что разработчику нужно вручную устанавливать его один за другим на стороне сервера. Если вы используете объект, это не будет так хлопотно, потому что SpringMVC автоматически заполнит объект возвращаемыми данными при переходе на страницу.
На этом этапе сначала измените страницу student.jsp:
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${student.id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${student.name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${student.email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${student.age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
Обратите внимание, что в предварительно заполненных данных есть лишний префикс student. Этот студент является именем переменной сервера, принимающего данные, и имя переменной сервера и студента здесь должно совпадать. Сервер определяется следующим образом:
@RequestMapping("/addstudent")
public String addStudent(@Validated(ValidationGroup2.class) Student student, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName()+":"+allError.getDefaultMessage());
}
return "student";
}
return "hello";
}
Обратите внимание, что серверу не нужно ничего делать, просто скажите, какая страница должна быть возвращена, и переменная студента будет автоматически заполнена в возвращаемой модели. Имя переменной является ключевым при заполнении. Если вы хотите настроить этот ключ, вы можете написать модель в параметре, а затем вручную добавить объект студента, как эхо простого типа данных.
Другой способ определить псевдоним эхо-переменной — использовать аннотацию @ModelAttribute.
11.2 @ModelAttribute
Аннотация @ModelAttribute имеет две основные функции:
- Определить псевдонимы для переменных при отображении данных
- определить глобальные данные
11.2.1 Определение псевдонимов
Когда данные отображаются, очень легко определить псевдонимы для переменных, просто добавьте эту аннотацию напрямую:
@RequestMapping("/addstudent")
public String addStudent(@ModelAttribute("s") @Validated(ValidationGroup2.class) Student student, BindingResult result) {
if (result != null) {
//校验未通过,获取所有的异常信息并展示出来
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getObjectName()+":"+allError.getDefaultMessage());
}
return "student";
}
return "hello";
}
После того, как это определение будет завершено, когда внешний интерфейс снова получит доступ к эхо-переменной, имя переменной будет не student, а s:
<form action="/addstudent" method="post">
<table>
<tr>
<td>学生编号:</td>
<td><input type="text" name="id" value="${s.id}"></td>
</tr>
<tr>
<td>学生姓名:</td>
<td><input type="text" name="name" value="${s.name}"></td>
</tr>
<tr>
<td>学生邮箱:</td>
<td><input type="text" name="email" value="${s.email}"></td>
</tr>
<tr>
<td>学生年龄:</td>
<td><input type="text" name="age" value="${s.age}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
11.2.2 Определение глобальных данных
Предположим, что в контроллере много методов, каждый метод будет возвращать данные во внешний интерфейс, но данные, возвращаемые каждым методом во внешний интерфейс, не совпадают, хотя и не одинаковы, но есть некоторые общие части без возвращаемого значения. метода. Эти общедоступные части могут быть извлечены и инкапсулированы отдельно в метод, помеченный аннотацией @ModelAttribute.
Например, в контроллере добавьте следующий код:
@ModelAttribute("info")
public Map<String,Object> info() {
Map<String, Object> map = new HashMap<>();
map.put("username", "javaboy");
map.put("address", "www.javaboy.org");
return map;
}
Когда пользователь обращается к любому методу в текущем контроллере, при возврате данных возвращаемое значение метода, аннотированного с помощью @ModelAttribute, будет вместе возвращено во внешний интерфейс. Информация в аннотации @ModelAttribute представляет ключ возвращаемых данных.
12.1 Возврат JSON
В настоящее время существует три основных инструмента обработки JSON:
- jackson
- gson
- fastjson
В SpringMVC предусмотрена соответствующая поддержка как для jackson, так и для gson, то есть, если вы используете эти два как конвертеры JSON, вам нужно только добавить соответствующие зависимости, и возвращаемые объекты и возвращаемые коллекции, Maps и т.д. будут автоматически преобразованы в JSON, однако, если вы используете fastjson, помимо добавления соответствующих зависимостей, вам также необходимо вручную настроить конвертер HttpMessageConverter самостоятельно. На самом деле первые два тоже используют конвертер HttpMessageConverter, но он автоматически предоставляется SpringMVC, а SpringMVC не предоставляет соответствующего конвертера для fastjson.
12.1.1 jackson
jackson — это инструмент обработки JSON, который широко используется и требует много времени.Чтобы использовать jackson в SpringMVC, вам нужно только добавить зависимости jackson:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>
После успешного добавления зависимости все объекты, коллекции и т. д., напрямую возвращенные в интерфейс, будут автоматически преобразованы в JSON. следующее:
public class Book {
private Integer id;
private String name;
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
@RequestMapping("/book")
@ResponseBody
public Book getBookById() {
Book book = new Book();
book.setId(1);
book.setName("三国演义");
book.setAuthor("罗贯中");
return book;
}
Здесь возвращается объект, но то, что получено на внешнем интерфейсе, представляет собой строку JSON, которая автоматически преобразуется в строку JSON с помощью HttpMessageConverter.
Если вы хотите вернуть массив JSON, напишите его следующим образом:
@RequestMapping("/books")
@ResponseBody
public List<Book> getAllBooks() {
List<Book> list = new ArrayList<Book>();
for (int i = 0; i < 10; i++) {
Book book = new Book();
book.setId(i);
book.setName("三国演义:" + i);
book.setAuthor("罗贯中:" + i);
list.add(book);
}
return list;
}
Добавление jackson может автоматически возвращать JSON, который зависит от класса с именем HttpMessageConverter, который сам по себе является интерфейсом. Как видно из названия, его роль — преобразователь сообщений Http. Поскольку это преобразователь сообщений, он выполняет две функции:
- Преобразование возвращенного объекта в JSON
- Преобразование JSON, отправленного внешним интерфейсом, в объект
Однако HttpMessageConverter — это всего лишь интерфейс, и соответствующие реализации предоставляются различными инструментами JSON, в jackson имя реализации — MappingJackson2HttpMessageConverter, а инициализация этой штуки выполняется SpringMVC. Если у вас нет особых требований к конфигурации, вам, как правило, не нужно самостоятельно предоставлять MappingJackson2HttpMessageConverter.
Для простого сценария приложения, например, у каждой книги есть дата публикации, измените класс Book следующим образом:
public class Book {
private Integer id;
private String name;
private String author;
private Date publish;
public Date getPublish() {
return publish;
}
public void setPublish(Date publish) {
this.publish = publish;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
Затем добавьте свойство даты при создании книги:
@RequestMapping("/book")
@ResponseBody
public Book getBookById() {
Book book = new Book();
book.setId(1);
book.setName("三国演义");
book.setAuthor("罗贯中");
book.setPublish(new Date());
return book;
}
Получите доступ к интерфейсу /book, возвращаемый формат json выглядит следующим образом:
Если мы хотим настроить формат возвращаемой даты, простой способ — добавить аннотации для достижения:
public class Book {
private Integer id;
private String name;
private String author;
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai")
private Date publish;
Обратите внимание, что здесь должен быть установлен часовой пояс.
Таким образом, вы можете настроить возвращаемый формат даты.
Однако у этого метода есть недостаток: эту аннотацию можно добавлять к атрибутам или классам, то есть ее можно применять не более чем ко всем атрибутам даты в классе. Если в проекте много классов сущностей, которым необходимо выполнять форматирование даты, использовать этот метод сложнее. В настоящее время мы можем предоставить экземпляр HttpMesageConverter Джексона самостоятельно. В этом случае мы можем настроить соответствующие свойства Конфигурация здесь будет глобальной конфигурацией.
В файл конфигурации SpringMVC добавьте следующую конфигурацию:
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" id="httpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
<property name="timeZone" value="Asia/Shanghai"/>
</bean>
</property>
</bean>
После завершения добавления удалите аннотации форматирования даты в классе сущностей Book, а затем проверьте результаты.
12.1.2 gson
gson — парсер JSON, запущенный Google, который в основном используется в разработке для Android, однако он также поддерживается в веб-разработке, а SpringMVC также предоставляет соответствующую автоматическую настройку для Gson, так что нам нужно только добавить зависимости gson в проект, вы можете напрямую использовать gson для разбора JSON.
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
Если в проекте есть и jackson, и gson, то по умолчанию используется jackson, почему? В конструкторе класса org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter порядок загрузки таков: сначала загружать HttpMessageConverter Джексона, а затем загружать HttpMessageConverter gson.
После добавления зависимостей вы можете напрямую вернуть строку JSON. При использовании Gson, если вы хотите выполнить пользовательскую настройку, вам необходимо настроить файл HttpMessageConverter.
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter" id="httpMessageConverter">
<property name="gson">
<bean class="com.google.gson.Gson" factory-bean="gsonBuilder" factory-method="create"/>
</property>
</bean>
<bean class="com.google.gson.GsonBuilder" id="gsonBuilder">
<property name="dateFormat" value="yyyy-MM-dd"/>
</bean>
12.1.3 fastjson
fastjson известен как самый быстрый анализатор JSON, но он также содержит наибольшее количество ошибок среди трех. SpringMVC не предоставляет соответствующий HttpMessageConverter для fastjson, поэтому при использовании fastjson вы должны вручную настроить HttpMessageConverter самостоятельно (если первые два не имеют особых потребностей, просто добавьте зависимости напрямую).
С помощью fastjson мы сначала добавляем зависимость fastjson:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
Затем настройте HttpMessageConverter в файле конфигурации SpringMVC:
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" id="httpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="dateFormat" value="yyyy-MM-dd"/>
</bean>
</property>
</bean>
Fastjson по умолчанию искажает китайский язык, добавьте следующую конфигурацию для решения:
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" id="httpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="dateFormat" value="yyyy-MM-dd"/>
</bean>
</property>
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
12.2 Получение JSON
Параметры, отправляемые браузером, могут быть в виде ключа/значения или строки JSON. В Jsp/Servlet мы получаем параметры в виде ключ/значение, обычно через метод getParameter. Если продавец на стороне клиента страдает от данных JSON, мы можем разобрать их в следующем формате:
@RequestMapping("/addbook2")
@ResponseBody
public void addBook2(HttpServletRequest req) throws IOException {
ObjectMapper om = new ObjectMapper();
Book book = om.readValue(req.getInputStream(), Book.class);
System.out.println(book);
}
Но этот метод парсинга немного громоздкий, в SpringMVC мы можем быстро преобразовать строку JSON в объект через аннотацию:
@RequestMapping("/addbook3")
@ResponseBody
public void addBook3(@RequestBody Book book) {
System.out.println(book);
}
Таким образом, вы можете напрямую получать строку JSON из внешнего интерфейса. Это также вторая функция, предоставляемая HttpMessageConverter.
13. RESTful
Этот раздел выбран из внешнего блога, исходная ссылка:Вууху. Руан Ифэн.com/blog/2011/0…
Все больше и больше людей начинают понимать, что веб-сайт — это программное обеспечение, и это новый тип программного обеспечения. Это «интернет-программное обеспечение» использует модель «клиент-сервер», построено на распределенной системе, взаимодействует через Интернет и характеризуется высокой задержкой и высоким уровнем параллелизма. Разработка веб-сайта, можно использовать модель разработки программного обеспечения. Но традиционно программное обеспечение и сеть — это две разные области, мало пересекающиеся; разработка программного обеспечения в основном предназначена для автономной среды, а работа в сети — это изучение связи между системами. Возникновение Интернета объединило эти две области, и теперь мы должны подумать о том, как разработать программное обеспечение для использования в среде Интернета.
Архитектура RESTful в настоящее время является самой популярной архитектурой программного обеспечения в Интернете. Он имеет четкую структуру, соответствует стандартам, прост для понимания и легко расширяется, поэтому его используют все больше и больше веб-сайтов.
Однако, что такое архитектура RESTful, сформулировать непросто. Далее я расскажу об архитектуре RESTful, как я ее понимаю. ,
RESTful Это не конкретная архитектура, не программное обеспечение, не фреймворк, а спецификация. До появления мобильного Интернета мы редко упоминали RESTful, главным образом потому, что он редко использовался. только Мы только разрабатываем веб-сайт, но также соответствуем нескольким интерфейсам (Android, iOS, HTML5 и т. д.) В настоящее время, когда мы проектируем внутренний интерфейс, нам необходимо учитывать форму, формат, параметр передача и многие другие вопросы интерфейса.
13.1 Происхождение
Термин REST был введен Роем Томасом Филдингом в его докторской диссертации в 2000 году.
Филдинг — очень важный человек, он главный разработчик протокола HTTP (версии 1.0 и 1.1), один из авторов серверного программного обеспечения Apache и первый председатель Apache Foundation. Поэтому, как только его статья была опубликована, она привлекла внимание и оказала непосредственное и далеко идущее влияние на развитие Интернета.
Он описал цель написания статьи следующим образом:
«В этой статье рассматривается пересечение двух основных границ в компьютерных науках — программного обеспечения и сетей. Долгое время исследования программного обеспечения были в основном сосредоточены на классификации дизайна программного обеспечения, эволюции методов проектирования и редко объективно оценивали влияние различных дизайнов. Напротив, сетевые исследования сосредоточены на деталях поведения связи между системами и на том, как улучшить производительность конкретного механизма связи, часто игнорируя тот факт, что изменение стиля взаимодействия приложения более важно, чем изменение протокола взаимодействия для него. вся производительность имеет большее влияние.Цель написания этой статьи состоит в том, чтобы понять и оценить архитектурный дизайн сетевого прикладного программного обеспечения в соответствии с принципами архитектуры, чтобы получить мощный, высокопроизводительный и подходящее коммуникационное программное обеспечение. Архитектура».
13.2 Имя
Филдинг назвал свои архитектурные принципы программного обеспечения для Интернета REST, сокращенно от Representational State Transfer. Мой перевод этой фразы - «переход состояния на уровне представления».
Если архитектура соответствует принципам REST, она называется архитектурой RESTful.
Лучший способ понять архитектуру RESTful — это понять, что на самом деле означает фраза Representational State Transfer и что означает каждое из ее слов. Если вы понимаете название, нетрудно понять, что это за дизайн REST.
13.3 Ресурсы
В названии REST «Преобразование состояния уровня представления» тема опущена. «Уровень представления» фактически относится к «слою представления» «Ресурсов».
Так называемый «ресурс» представляет собой объект в сети или конкретную информацию в сети. Это может быть текст, картинка, песня, служба, словом, конкретная реальность. Вы можете указать на него с помощью URI (унифицированного указателя ресурсов), и каждый ресурс соответствует определенному URI. Чтобы получить этот ресурс, просто посетите его URI, чтобы URI стал адресом или уникальным идентификатором для каждого ресурса.
Так называемый «серфинг в Интернете» означает взаимодействие с рядом «ресурсов» в Интернете и вызов их URI.
В приложении RESTful каждый URI представляет ресурс.
13.4 Представление
«Ресурс» — это информационная сущность, которая может иметь множество внешних проявлений. Форма, в которой мы конкретно представляем «ресурс», называется его «слоем представления» (Representation).
Например, текст может быть представлен в формате txt, формате HTML, формате XML, формате JSON или даже в двоичном формате; изображения могут быть представлены в формате JPG или PNG.
URI представляет только сущность ресурса, а не его форму. Строго говоря, суффикс «.html» в конце некоторых URL-адресов не нужен, потому что этот суффикс указывает на формат и относится к категории «уровень представления», а URI должен представлять только расположение «ресурса». Его конкретная форма должна быть указана в информации заголовка HTTP-запроса с полями Accept и Content-Type, которые являются описанием «уровня представления».
13.5 Передача состояний
Посещение веб-сайта представляет собой интерактивный процесс между клиентом и сервером. В этом процессе обязательно будут задействованы данные и изменения состояния.
Протокол связи через Интернет Протокол HTTP является протоколом без сохранения состояния. Это означает, что все состояние сохраняется на стороне сервера. Следовательно, если клиент хочет управлять сервером, он должен использовать некоторые средства для осуществления «передачи состояния» на стороне сервера. И это преобразование основано на уровне представления, так что это «преобразование состояния уровня представления».
Средством, используемым клиентом, может быть только протокол HTTP. В частности, в протоколе HTTP есть четыре глагола, выражающие режим работы: GET, POST, PUT, DELETE. Они соответствуют четырем основным операциям:
- GET используется для получения ресурсов
- POST используется для создания новых ресурсов (также может использоваться для обновления ресурсов)
- PUT используется для обновления ресурсов
- DELETE используется для удаления ресурсов
13.6 Обзор
Основываясь на приведенном выше объяснении, давайте резюмируем, что такое архитектура RESTful:
- Каждый URI представляет ресурс;
- Между клиентом и сервером проходит какой-то уровень представления этого ресурса;
- Клиент работает с ресурсами на стороне сервера с помощью четырех HTTP-команд для достижения «преобразования состояния уровня представления».
13.7 Непонимание
Архитектура RESTful имеет несколько типичных ошибок проектирования.
Одна из самых распространенных ошибок проектирования — когда URI содержат глаголы. Поскольку «ресурс» представляет сущность, это должно быть существительное, URI не должны содержать глаголы, а глаголы должны быть помещены в протокол HTTP.
Например, URI — это /posts/show/1, где show — это глагол, этот URI неправильно разработан, правильное написание должно быть /posts/1, а затем используйте метод GET для указания show.
Если какое-то действие не может быть выражено HTTP-глаголами, вы должны сделать это действие ресурсом. Например, онлайн-перевод, перевод 500 юаней со счета 1 на счет 2, неверный URI:
- POST /accounts/1/transfer/500/to/2
Правильный способ его написания — заменить глагол «передача» на существительное «транзакция».Ресурс не может быть глаголом, но может быть услугой:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
Еще одно недоразумение в дизайне — добавление номера версии к URI:
Поскольку разные версии можно понимать как разные представления одного и того же ресурса, следует использовать один и тот же URI. Номера версий можно различить в поле Accept заголовков HTTP-запросов (см. Управление версиями служб REST):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
13.8 Поддержка SpringMVC
SpringMVC обеспечивает очень полную поддержку RESTful, в основном включая следующие аннотации:
- @RestController
Эта аннотация является составной аннотацией:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
В общем, используйте @RestController напрямую, чтобы пометить контроллер, вы не можете использовать @Controller.
Среди методов запроса предусмотрены общие методы запроса:
- @PostMapping
- @GetMapping
- @PutMapping
- @DeleteMapping
Также есть аннотация @PathVariable, которая извлекает параметры в адресе запроса:
@GetMapping("/book/{id}")//http://localhost:8080/book/2
public Book getBookById(@PathVariable Integer id) {
Book book = new Book();
book.setId(id);
return book;
}
Параметр 2 будет передан в переменную id.
14. Доступ к статическим ресурсам
В SpringMVC по умолчанию перехватываются статические ресурсы, такие как html, js, css, jpg, png, txt, pdf и т. д., к которым нет прямого доступа. Поскольку все запросы перехватываются, нам необходимо выполнить дополнительную обработку для статических ресурсов.Метод обработки очень прост.Добавьте следующее содержимое непосредственно в файл конфигурации SpringMVC:
<mvc:resources mapping="/static/html/**" location="/static/html/"/>
Mapping указывает правило сопоставления, а также правило перехвата, то есть, если адрес запроса имеет формат /static/html, то соответствующий ресурс будет искаться в каталоге /static/html/.
В определении пути сопоставления есть два * в конце, которые являются символом сопоставления пути в стиле Ant, и есть три подстановочных знака:
подстановочный знак | значение |
---|---|
** | сопоставление многослойных путей |
* | сопоставить путь |
? | соответствует любому одиночному символу |
Более примитивная конфигурация может быть следующей:
<mvc:resources mapping="/static/html/**" location="/static/html/"/>
<mvc:resources mapping="/static/js/**" location="/static/js/"/>
<mvc:resources mapping="/static/css/**" location="/static/css/"/>
Однако, поскольку ** может представлять многоуровневые пути, мы можем упростить приведенную выше конфигурацию:
<mvc:resources mapping="/**" location="/"/>
15. Перехватчики
Перехватчик в SpringMVC эквивалентен фильтру в Jsp/Servlet, но функция перехватчика более мощная.
Определение перехватчика очень просто:
@Component
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 这个是请求预处理的方法,只有当这个方法返回值为 true 的时候,后面的方法才会执行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1:preHandle");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1:postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1:afterCompletion");
}
}
@Component
public class MyInterceptor2 implements HandlerInterceptor {
/**
* 这个是请求预处理的方法,只有当这个方法返回值为 true 的时候,后面的方法才会执行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor2:preHandle");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2:postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor2:afterCompletion");
}
}
После того, как перехватчик определен, его необходимо настроить в конфигурационном файле SpringMVC:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="myInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="myInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
Если перехватчиков несколько, правила перехвата следующие:
- preHandle вызывается в порядке определения перехватчика
- postHandler вызывается в порядке, обратном определению перехватчика.
- afterCompletion вызывается в обратном порядке определения перехватчика
- postHandler возвращает успешный вызов всем перехватчикам в цепочке перехватчиков.
- afterCompletion вызывается, только если preHandle возвращает true