Валидация форм в Spring MVC

Валидация – это проверка данных формы на то подходят ли они под какие-то условия.

Для начала необходимо скачать hibernate validator.

Можно скачать по ссылке https://hibernate.org/validator/releases/6.2/. Потом закидываем в lib jar файлы из папки dist и required.

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

Давайте теперь дополним класс SomeUser. В этом классе создадим два новых поля и сгенерируем геттеры сеттеры для них. Эти новые поля будут связаны со своим текстовым полем в форме.

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

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

Итак разберем аннотации валидации:

package classes; import java.util.LinkedHashMap; @Component public class SomeUser { // Ниже используются аннотации валидации над полем. // В нашем приложении текстовое поле формы будет связано // с полем userName объекта в аттрибуте. // Если поле помечено @NotNull значит оно будет проверяться // на пустоту при отправке формы. То есть, если пользователь // ничего не ввел в текстовое поле, которое связано с полем // userName и отправил форму, то отправка формы не будет // успешной, пока пользователь не введет значение // в текстовое поле. // message — это сообщение, которое будет // показано пользователю, если он не ввел ничего в поле. // С помощью @Size можно указать минимальное количество // символов, которые пользователь должен ввести // в текстовое поле. @NotNull(message = “field is required!!!”) @Size(min = 5, message = “field is required!!!”) private String userName; private String userCountry; private String userLanguage; private String[] userBrowser; // Также форма будет содержать текстовое поле, которое // будет связано с полем userAge объекта в атрибуте. // С помощью @Min задаем минимальное число, которое // можно указать в текстовом поле. // С помощью @Max задаем максимальное число, которое // можно указать в текстовом поле. @Min(value = 0, message = “must be more than 0”) @Max(value = 100, message = “must be less than 100”) private Integer userAge; // Также форма будет содержать текстовое поле, которое // будет связано с полем userEmail объекта в атрибуте. // С помощью @Pattern можно задать регулярное выражение, // по которому будет проверяться содержимое текстового // поля. Регулярные выражения мы пока не проходили. // Но если вкратце, то это некоторое правило // (оно может быть достаточно сложным), по которому // валидируется содержимое поля. // В нашем случае мы задали, что текстовое поле должно // содержать 30 символов и может содержать // такие-то символы. @Pattern(regexp = “^[a-zA-Z0-9]{30}”, message = “must be 30 characters”) private String userEmail; public SomeUser() { } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserCountry() { return userCountry; } public void setUserCountry(String userCountry) { this.userCountry = userCountry; } public String getUserLanguage() { return userLanguage; } public void setUserLanguage(String userLanguage) { this.userLanguage = userLanguage; } public String[] getUserBrowser() { return userBrowser; } public void setUserBrowser(String[] userBrowser) { this.userBrowser = userBrowser; } public Integer getUserAge() { return userAge; } public void setUserAge(Integer userAge) { this.userAge = userAge; } public String getUserEmail() { return userEmail; } public void setUserEmail(String userEmail) { this.userEmail = userEmail; } }

Давайте теперь свяжем новые поля в объекте в аттрибуте username c новыми тегами в форме на странице. Также с помощью тега form:errors будут выводиться сообщения, которые мы писали в message в классе у полей. Этот тег пишем там, где должно появляться это сообщение на странице.

Страница с формой:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%@ page isELIgnored="false" %> Insert title here username:
Age:
Email:

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

package classes; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping(“/FirstController”) public class MVCcontroller { @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; } @RequestMapping(“/FormPage”) public String FormPage() { return “FormPage”; } @RequestMapping(“/FormProcessingController”) public String FormProcessingController( HttpServletRequest req, Model model) { model.addAttribute(“resposeMsg”, (req.getParameter(“username”) + ” entered!”)); return “FormProcessingPage”; } @RequestMapping(“/FormTagPage”) public String FormTagPage(Model model) { model.addAttribute(“someuser”, new SomeUser()); return “FormTagPage”; } @RequestMapping(“/FormTagProcessor”) public String FormTagProcessor( @ModelAttribute(“username”) SomeUser someUser) { System.out.println(someUser.getUserName()); System.out.println(someUser.getUserCountry()); System.out.println(someUser.getUserLanguage()); System.out.println(someUser.getUserBrowser()); return “FormTagProcessingPage”; } // Новый обработчик // он просто переводит на страницу с формой, // которая будет валидироваться. @RequestMapping(“/FormValidPage”) public String FormValidPage(Model model) { model.addAttribute(“someuser”, new SomeUser()); return “FormValidPage”; } // Новый обработчик @RequestMapping(“/FormValidProcessor”) // аннотацией @Valid указываем что пришедший атрибут // из формы в обработчик подлежит валидации и в нем // будут проверены поля с аннотациями валидации public String FormValidProcessor( @Valid @ModelAttribute(“someuser”) SomeUser someUser, BindingResult mybindingResult) { // Результат же валидации атрибута // записывается в mybindingResult. // проверяем bindingResult на наличие ошибок валидации if (mybindingResult.hasErrors()) { // если есть ошибки валидации отправляем уже // валидированный атрибут обратно в форму где // ошибка валидации будет отображена тегом form:errors return “FormValidPage”; } else { // если их нет то отправляем атрибут // на результирующую страницу где из него // выведем содержимое нужной переменной return “FormValidProcessingPage”; } } }

На странице на которую перенаправляет пользователя обработчик (если данные в объекте из аттрибута оказались валидными) извлечем выбранные пользователем значения из объекта в аттрибуте username.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page isELIgnored="false"%> Auth Form

${someuser.userName}

${someuser.userAge}

${someuser.userEmail}

Перейдем на страницу с формой через обработчик по пути /FormValidPage. Первое текстовое поле давайте оставим пустым, во втором введем число 144, в третьем введем меньше 30 символов.

и после нажатия Submit:

Видим, что вывелись сообщения, которые мы писали в message в аннотациях о том, что пользователь ввел неверные данные. О том, что пользователь оставил поле пустым, а оно не должно быть пустым, о том, что он ввел число выше ста и о том, что поле должно содержать ровно 30 символов.

То есть очевидно аннотации NotNull, Max и Pattern работают.

Давайте теперь проверим аннотации Size и Min. Введем в перовое поле меньше 5 символов, а во второе поле число меньше 0.

После нажатия Submit:

Видим, что сообщения, которые мы писали в аннотациях Size и Min вывелись.  Значит, что аннотации работают.      

Теперь давайте введем правильные данные, то есть в перове поле больше 5 букв, во второе поле число между 0 и 100, в третье поле ровно 30 символов.  

Вот так:

Нажимаем Submit:

Как видим, обработчик успешно перевел нас на вторую страницу и отобразил на ней введенные данные поскольку введенные данные были валидными.

Это были основные аннотации валидации. Есть и другие конечно.

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

Теги Spring Form: Select, Radio, Checkbox

Помимо тега form:input, рассмотренного в прошлом уроке, есть также и другие теги с которыми мы можем связывать поля объекта в аттрибуте.

В этом уроке рассмотрим теги form:select, form:radio и form:checkbox.

Эти теги аналоги всем известных html тегов, которые предоставляют пользователю варианты выбора.

Напомним как они выглядят:

Добавим в класс объекта, который будет в аттрибуте, новые поля. Эти поля будут связываться с новыми тегами в форме так же, как в прошлом уроке связывался form:input с полем объекта.

package classes; import java.util.LinkedHashMap; import org.springframework.stereotype.Component; @Component public class SomeUser{ private String userName; //создадим поле, в которое будет записываться //то что выбрал пользователь в form:select private String userCountry; //в это поле сохр. значение form:radiobutton private String userLanguage; //в этот массив сохраняются //значения form:checkbox. Массив это //потому что пользователь может выбрать //несколько значений checkbox. private String[] userBrowser; public SomeUser() { } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserCountry() { return userCountry; } public void setUserCountry(String userCountry) { this.userCountry = userCountry; } public String getUserLanguage() { return userLanguage; } public void setUserLanguage(String userLanguage) { this.userLanguage = userLanguage; } public String[] getUserBrowser() { return userBrowser; } public void setUserBrowser(String[] userBrowser) { this.userBrowser = userBrowser; } }

Давайте теперь свяжем новые поля в объекте в аттрибуте username c новыми тегами в форме на странице.

Страница с формой:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%@ page isELIgnored="false" %> Insert title here username:

English Ukrainian Russian
Mozilla Chrome Edge

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

package classes; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping(“/FirstController”) public class MVCcontroller{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; } @RequestMapping(“/FormPage”) public String FormPage() { return “FormPage”; } @RequestMapping(“/FormProcessingController”) public String FormProcessingController( HttpServletRequest req, Model model) { model.addAttribute(“resposeMsg”, (req.getParameter(“username”)+” entered!”)); return “FormProcessingPage”; } @RequestMapping(“/FormTagPage”) public String FormTagPage(Model model) { model.addAttribute(“someuser”,new SomeUser()); return “FormTagPage”; } @RequestMapping(“/FormTagProcessor”) public String FormTagProcessor( @ModelAttribute(“username”) SomeUser someUser) { System.out.println(someUser.getUserName()); //теперь выведем и выбранные пользователем //значения в консоль. System.out.println(someUser.getUserCountry()); System.out.println(someUser.getUserLanguage()); System.out.println(someUser.getUserBrowser()); //а также они будут отправлены на страницу //FormTagProcessorPage.jsp и выведены там return “FormTagProcessingPage”; } }

На странице на которую перенаправляет пользователя обработчик извлечем выбранные пользователем значения из объекта в аттрибуте username.

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

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page isELIgnored="false" %> Auth Form

${username.userName}

${username.userCountry}

${username.userLanguage}

${listelement}

Перейдем на страницу с формой через обработчик по пути /FormTagPage и выберем значения у тегов.

Нажмем submit.

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

Специальный тег Form в Spring

В спринг есть специальные теги, которые упрощают нам разработку.

В этом уроке рассмотрим специальный тег form:form.

Тег form:form обеспечивает прямую привязку полей объекта модели вашего приложения к полям ввода HTML-формы.

Давайте создадим простой класс SomeUser с одним полем, геттерами и сеттерами.

package classes; import org.springframework.stereotype.Component; @Component public class SomeUser { // определим поле, которое в объекте SomeUser // будет напрямую привязано к текстовому // полю в форме. и сеттеры геттеры для него private String userName; public SomeUser() { } }

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

Так вот, Eclipse может сам генерировать геттеры и сеттеры для полей в классе. Для этого нужно нажать правой кнопкой мыши в любом месте класса без кода и выбрать Generate Getters and Setters.

Отмечаем поле для которого нужно сгенерировать геттер сеттер и нажимаем generate.

Как видим, магическим образом в классе появились геттер и сеттер для поля userName и писать вручную ничего не пришлось.

package classes; import org.springframework.stereotype.Component; @Component public class SomeUser { // определим поле которое в объекте SomeUser // будет напрямую привязано к текстовому // полю в форме, и сеттеры геттеры для него private String userName; public SomeUser() { } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }

Генерировать можно не только геттеры и сеттеры.

Можно, например, сгенерировать переопределения методов Оbject для данного класса, таких как ToString, equals и hashCode, что очень удобно.

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

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

Этот обработчик помимо того, что создает аттрибут c объектом SomeUser будет перенаправлять на страницу с формой, где этот объект в аттрибуте будет связываться с формой.

Новый обработчик:

package classes; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping(“/FirstController”) public class MVCcontroller{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; } @RequestMapping(“/FormPage”) public String FormPage() { return “FormPage”; } @RequestMapping(“/FormProcessingController”) public String FormProcessingController( HttpServletRequest req, Model model) { model.addAttribute(“responseMsg”, (req.getParameter(“username”)+” entered!”)); return “FormProcessingPage”; } // Новый обработчик @RequestMapping(“/FormTagPage”) public String FormTagPage(Model model) { // здесь создаем аттрибут, передавая в него // экземпляр, с которым будет связана форма // на странице FormTagPage model.addAttribute(“someuser”,new SomeUser()); return “FormTagPage”; } }

Давайте же свяжем форму на странице на которую переводит только что определенный обработчик /FormTagPage и объект в аттрибуте в модели.

Страница с формой:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%@ page isELIgnored="false" %> Insert title here

Теперь давайте создадим этот обработчик, который был прописан в теге action у формы.

м package classes; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping(“/FirstController”) public class MVCcontroller{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; } @RequestMapping(“/FormPage”) public String FormPage() { return “FormPage”; } @RequestMapping(“/FormProcessingController”) public String FormProcessingController( HttpServletRequest req, Model model) { model.addAttribute(“responseMsg”, req.getParameter(“username”)+” entered!”); return “FormProcessingPage”; } @RequestMapping(“/FormTagPage”) public String FormTagPage(Model model) { model.addAttribute(“someuser”,new SomeUser()); return “FormTagPage”; } //Новый обработчик @RequestMapping(“/FormTagProcessor”) //с помощью аннотации @ModelAttribute принимаем атрибут //посланный формой в этот обработчик и помещаем //экземпляр из него в новый экземпляр someUser public String FormTagProcessor( @ModelAttribute(“username”) SomeUser someUser) { //Воспользуемся этим объектом. Проверим //содержится ли в поле userName этого объекта то //что вводилось в текстовое поле в форме. //Выведем на консоль томcat содержимое поля //userName в новом объекте someUser System.out.println(someUser.getUserName()); //Далее переданынй в тот обработчик объект(аттребут) //доступен на jsp странице, на которую //происходит переход из этого обработчика. //Тоесть на FormTagProcessingPage. return “FormTagProcessingPage”; } }

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

м <%@ page isELIgnored="false" %> Auth Form

${username.userName}

Проверка работы приложения.

Перейдем на страницу с формой через обработчик по пути /FormTagPage.

Введем в поле текст и нажмем submit.

Видим в консоли Tomcat, что новый объект в обработчике /FormTagProcessor содержит то, что вводил в текстовое поле формы пользователь.

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

Обработка дынных формы в Spring MVC, передача данных в Model

Давайте создадим приложение в котором:

  1. Пользователь вводит какие-то данные в html форму
  2. отправляет их в обработчик
  3. этот обработчик принимает эти данные и перенаправляет пользователя на другую страницу, при этом отправляя пользователю некоторые данные на эту страницу.

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

Страница формы очень простая. Она передает параметр username, содержащий введенные данные пользователем в текстовое поле, в обработчик по пути /FormProcessingController имя которого указывается в action. Передача происходит get запросом.

Страница с формой:

Auth Form
username:

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

package classes; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping(“/FirstController”) public class MVCcontroller{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; //имя jsp } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; //имя jsp } //FormPage обработчик нужен просто чтобы //перейти на страницу на которой будет форма, //в которую клиент сможет вводить данные @RequestMapping(“/FormPage”) public String FormPage() { return “FormPage”; //имя формы } //Название обработчика ниже как мы помним //было указано в теге action у формы. //Этому обработчику будет передаваться параметр //и он будет перенаправлять на вторую страницу //на которой будет выводиться переданный //через форму параметр. @RequestMapping(“/FormProcessingController”) //метод-обработчик может иметь разные параметры //например HttpServletRequest req – это тот самый req //что и в сервлетах и работать с ним мы можем так же //как и в сервлетах. Будем извлекать из него параметры. //а в Model можем передавать данные как //в response в сервлете. То есть как и в response //в сервлетах можем положить в model аттрибут, который //будет доступен на странице на которую этот //обработчик перенаправляет. public String FormProcessingController( HttpServletRequest req, Model model) { //Ниже всем известный из сервлетов getParameter. //Получаем имя параметра переданный сюда из формы //и как в response создаем аттрибут в модели //значение которого будет принятый параметр //плюс дополнительная строка entered!. model.addAttribute(“responseMsg”, (req.getParameter(“username”)+” entered!”)); //Ниже имя jsp на которую отправляется //параметр в аттрибуте модели return “FormProcessingPage”; } }

На странице на которую перенаправляет обработчик просто выведем с помощью EL выражения положенный в модель аттрибут. Раньше таким образом мы уже выводили аттрибуты переданные из сервлета в resp.

<%@ page isELIgnored="false" %> Form

${resposeMsg}

Перейдем на страницу с формой через обработчик по пути /FormPage.

Введем в поле текст и нажмем submit.

Видим, что обработчик успешно перенаправил на вторую страницу и параметр был успешно передан этим обработчиком на эту страницу.

Можно увидеть в адресной строке переданный параметр username=Mike.

Search Icon

Как мы помним, параметр видно в адресной строке, потому что совершался get запрос, но желательно передавать post запрос, чтобы параметра не было видно в адресной строке.

Аннотация RequestMapping в Spring MVC

Классов контроллеров в MVC-приложении может быть много. Давайте создадим еще один контроллерMVController2.

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

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

Для этого RequestMapping нужно прописать над классом контроллером и передать этой аннотации путь к этому классу контроллеру также, как мы писали эту аннотацию над методами обработчиками.

Первый класс контроллер:

package classes; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller //Внизу пишем путь к этому классу контроллеру. //Теперь если клиент захочет //перейти например к первому обработчику в этом //контроллере ему нужно будет перейти по адресу: //http://localhost:8080/SpringMVCap/FirstController/FirstJSP @RequestMapping(“/FirstController”) public class MVCcontroller{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; //имя jsp } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; //имя jsp } }

Второй класс контроллер:

package classes; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller //Внизу пишем путь к этому классу контроллеру. //Теперь если клиент захочет клиент захочет //перейти например к первому обработчику в этом //контроллере ему нужно будет перейти по адресу: //http://localhost:8080/SpringMVCap/SecondController/FirstJSP @RequestMapping(“/SecondController”) public class MVCcontroller2{ @RequestMapping(“/FirstJSP”) public String FirstJSP() { return “JSPpage”; //имя jsp } @RequestMapping(“/SecondJSP”) public String SecondJSP() { return “JSPpage1”; //имя jsp } }

Можно увидеть, что методы обработчики в обоих классах имеют одинаковые адреса            

То есть видим, что и в первом классе и во втором есть метод помеченный @RequestMapping("/FirstJSP").

Если бы мы не указали над классами RequestMapping, то DispatcherServlet не понял бы какой из методов обработчиков, помеченных путем /FirstJSP вызывать.

Перейдем к FirstJSP через первый контроллер.

Перейдем к FirstJSP через второй контроллер.

Как видим, обе jsp страницы открылись успешно через разные классы контроллеры.

Основы Spring MVC. Как работает DispatcherServlet

Что же такое Spring MVC?

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

Для понимания того, как работает Spring MVC необходимо вспомнить, что такое паттерн Front Controller.

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

В Spring MVC этим центральным обработчиком запросов является специальный сервлет, он называется DispatcherServelet. Этот сервлет есть в спринг библиотеке, которую мы уже добавили в наш проект в прошлом уроке, нам нужно будет лишь настроить его в web.xml.

Example

Если подробнее то Spring MVC работает так:

  1. Все запросы сначала идут на DispatcherServlet (Front Controller).
  2. Model – это то, в чем хранятся данные при передаче в DispatcherServet и из него.
  3. DispatcherServlet исходя из запроса клиента решает в какой другой контроллер перенаправить данные клиента.
  4. В контроллере, в который данные клиента были перенаправлены, эти данные обрабатываются, и новые сформированные данные в этом контроллере сохраняются в Model и передаются в DispatcherServlet.
  5. DispatcherServlet перенаправляет клиента, который послал запрос, на View, название которого также было передано ему из контроллера.

Как видим, все запросы и ответы идут через Front Controller.

Также, исходя из описания работы выше, и, собственно, из названия "Spring MVC", очевидно, что Spring MVC тоже реализует архитектру MVC, которую мы уже рассматривали ранее.

Давайте же сконфигурируем этот DispatcherServlet в web.xml. Конфигурируется он как самый обычный сервлет. Мы это уже делали раньше.

web.xml:

spring org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/applicationContext.xml 1 spring /

Давайте теперь добавим в проект некоторые файлы и папки.

В папке classes создадим класс MVCController (имя может быть любым). В этом классе как раз и будут находиться обработчики куда DispatcherServlet перенаправляет запросы.

В папку WEB-INF добавим папку jsp, в которой будут храниться страницы на которые будет перенаправлять клиента DispatcherServlet.

И также добавим, конечно же, applicationContext.xml, в котором определяются бины.

Теперь необходимо сконфигурировать applicationContext.xml. DispatcherServlet будет считывать этот файл и создавать бины.

/WEB-INF/jsp/ .jsp

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

package classes; import java.io.*; // Помечаем этот класс как контроллер. // То есть здесь будут находиться обработчики, // на которые DispatcherServlet будет перенаправлять // запросы клиентов. // В applicationContext мы указали, чтобы // пакет classes анализировался на наличие аннотаций. // DispatcherServlet будет создавать внутри себя // особый бин на основе класса, помеченного @Controller, // который будет передавать запросы пользователя // и возвращать название страницы // или объект класса Model с данными. @Controller public class MVCcontroller { // В этом классе можно увидеть два обработчика. // Если клиент вобьет у себя в браузере адрес // http://localhost:8080/SpringMVCap/FirstJSP // то вызовется первый метод в этом классе, // если http://localhost:8080/SpringMVCap/SecondJSP // то второй. Очевидно, что в @RequestMapping // указывается путь, по которому вызывается // обработчик. Можно вспомнить, что // путь к обработчику указывается в аннотации // @WebServlet над классом сервлета. @RequestMapping(“/FirstJSP”) // Название метода обработчика может быть любым. // Возвращать этот метод может Model // или название какой-то вьюхи. // Как уже было сказано, это название // возвращается из этого метода внутри // DispatcherServlet, и DispatcherServlet ищет // страницу с таким названием в папке, которая // ему была указана в applicationContext.xml. public String FirstJSP() { // имя jsp, на которую // переводит этот обработчик return “JSPpage”; } @RequestMapping(“/SecondJSP”) public String SecondJSP() { // имя jsp, на которую // переводит этот обработчик return “JSPpage1”; } // Как видим, для всех этих методов // есть общая область видимости в пределах класса, // что очень удобно, то есть можно создать поле // в классе и использовать его // в обоих методах-обработчиках. // Раньше такой общей области видимости не было, // поскольку каждый сервлет писался // в отдельном классе. }

Помним, что мы добавили в папку jsp два файла страницы. Давайте же перейдем по путям созданных обработчиков и проверим открываются ли эти страницы.

Для начала, запускаем сервер и перейдем в браузере по пути /FirstJSP первого обработчика.

Теперь перейдем в браузере по пути /SecondJSP второго обработчика.

Как видим, обе jsp страницы открылись успешно. Значит и обработчики, и DisparcherServlet, и всё остальное работает корректно.

Создание веб-приложения в Eclipse IDE

До данного урока мы компилировали и запускали приложения вручную через консоль. Давайте же с началом изучения темы spring MVC переместимся в удобную среду разработки приложений.

Одной из самых популярных является Eclipse IDE.

Скачать эту IDE можно по ссылке:

https://www.eclipse.org/downloads/packages/release/2023-09/r/eclipse-ide-enterprise-java-and-web-developers

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

Изменить режим можно через вкладку window:

В открывшемся окне выбираем Java EE. По идее этот режим должен стоять по дефолту.

Теперь давайте же создадим веб-приложение на основе которого в следующем уроке будем создавать Spring MVC приложение.

Во вкладке file выбираем Dynamic Web Project:

Далее в открывшемся окне даем проекту имя, а всё остальное оставляем по умолчанию и нажимаем finish.

Проект создан и теперь он появился слева в project explorer где его можно рассмотреть.

В структуре созданного проекта можно увидеть знакомые папки. Например WEB-INF и lib.

Эти папки будут использоваться для того же, для чего они использовались и раньше не в IDE.

Все остальные папки и файлы, типа папки classes, в которой хранятся java классы или файл web.xml, нужно добавить вручную.

Давайте добавим файлы и папки, которых не хватает. Добавим папку classes и файл web.xml.

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

Теперь же нам нужно как то запустить этот проект на сервере Tomcat. Для этого нам нужно указать нашей IDE, где на нашем компьютере находиться сервер Tomcat, чтобы Eclipse запускал на нем проекты.

Чтобы добавить в ide сервер, для начала, необходимо внизу во вкладке Servers нажать на синий текст:

В открывшемся окне выбираем Apache -> Tomcat 9.0 и внизу нажмем add, чтобы указать ide путь у серверу Tomcat 9.0

Нажимаем Browse и указываем путь к Tomcat. У нас он лежал на диске С, как помним.

Нажимаем Finish -> Finish.

Также нужно добавить в classpath библиотеки Tomcat в особенности servlet-api.jar, которым мы пользовались для компиляции сервлетов. Для этого нажимаем правой кнопкой мыши на проекте и нажимаем Properties. В открывшемся окне выделяем classpath и жмем Add Library.

Выбираем Server Runtime, нажимаем Next.

Выбираем сервер, который мы добавляли в Eclipse, нажимаем Finish -> Apply and Close.

Библиотеки добавлены.

Для запуска сервера и проверки проекта добавим в папку webapp файлик index.html с любым текстом внутри. Этот файл будет открываться когда мы запустим сервер и перейдем в браузере по корневому пути проекта. Корневой путь у проектов в Eclipse по умолчанию это название проекта. То есть переход в браузере ко всему, что есть в проекте будет начинаться с http://localhost:8080/SpringMVCap/.

Также необходимо добавить в файл web.xml некоторые базовые конфигурации, чтобы проект можно было запустить.


Проверка работы приложения.

На данном этапе это пока еще не Spring MVC приложение, пока это просто веб-приложение с которым мы уже работали раньше. Для того чтобы оно стало Spring MVC приложением, нужно будет в web.xml сконфигурировать особый сервлет и добавить в проект applicatioContext, но это будет рассмотрено в следующем уроке.

Давайте же протестируем работу приложения.

Нажмем правой кнопкой мыши на проекте и выберем Run on Server

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

В браузере теперь перейдем по корневому пути и можем увидеть содержимое файла index.html.

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