# Лекция 7-8

Тема:&#x20;Фреймворк Spring MVC. Создание MVC-приложения. Создание контроллера, переход между страницами. Использование Spring Expression Language. Создание представлений с помощью Thymeleaf.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Теоретические сведения

Spring MVC – веб-фреймворк, призванный упростить разработку веб-приложений. Опираясь на шаблон модель–представление–контроллер (Model-View-Controller, MVC), фреймворк Spring MVC помогает строить веб-приложения, столь же гибкие и слабо связанные, как сам фреймворк Spring.

**Схема работы фреймворка** **Spring** **MVC**

Схема работы фреймворка выглядит следующим образом:

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25WiEyx1itbDEVixXo%2Fimage.png?alt=media\&token=2125975d-f384-4c2a-8baf-b03fb6f6cbb1)

Краткое описание схемы работы Spring MVC звучит следующим образом:

* вначале **DispatcherServlet** (диспетчер сервлетов) получает запрос, далее он смотрит свои настройки, чтобы понять какой контроллер использовать (на рисунке **Handler** **Mapping**);
* после получения имени контроллера запрос передается на обработку в этот контроллер (на рисунке **Controller**). В контроллере происходит обработка запроса и обратно посылается **ModelAndView** (модель — сами данные; view (представление) — как эти данные отображать);
* **DispatcherServlet** на основании полученного **ModelAndView**, должен определить, какое представление будет выводить данные. Для этого используется **арбитр представлений** (**View** **Resolver**), который на основании полученного логического имени представления возвращает ссылку на файл **View**;
* в представление передаются данные (Model) и обратно, если необходимо, посылается ответ от представления.

Давайте рассмотрим этот процесс более подробно:

1. Когда запрос покидает браузер, он несет в себе информацию о требовании пользователя. По крайней мере, запрос будет нести в себе запрошенный URL. Но он может также нести дополнительные данные, такие как информация из формы, заполненной пользователем;
2. Первой остановкой на пути запроса является **DispatcherServlet**. Как и большинство веб-фреймворков на языке Java, фреймворк Spring MVC пропускает все входящие запросы через единственный сервлет входного контроллера. Входной контроллер (**front** **controller**) является типичным шаблоном проектирования веб-приложений, где единственный сервлет берет на себя ответственность за передачу всех запросов остальным компонентам приложения, выполняющим фактическую их обработку. В Spring MVC входным контроллером является **DispatcherServlet**;
3. Задача контроллера **DispatcherServlet** состоит в том, чтобы передать запрос контроллеру Spring MVC. **Контроллер – это компонент** **Spring, обрабатывающий запрос**. Но приложение может иметь несколько контроллеров, и входному контроллеру **DispatcherServlet** требуется помощь, чтобы определить, какому контроллеру передать запрос. Поэтому контроллер **DispatcherServlet** консультируется c одним или несколькими **механизмами отображения** (Handler Mapping) и выясняет, какой контроллер будет обрабатывать тот или иной запрос. При принятии решения механизм отображения в первую очередь руководствуется адресом URL в запросе;
4. Как только будет выбран соответствующий контроллер, **DispatcherServlet** отправляет запрос в путь к выбранному контроллеру. Достигнув контроллера, запрос отдаст часть своего груза (информацию, отправленную пользователем) и терпеливо будет ждать, пока контроллер обработает эту информацию. (На самом деле хорошо спроектированный контроллер сам почти не занимается обработкой информации, вместо этого он делегирует ответственность за обработку одному или нескольким служебным объектам);
5. В результате работы контроллера часто появляется некоторая информация, которая должна быть передана назад пользователю и отображена в браузере. Эта информация называется **моделью (Model)**. Но отправки обратно необработанной информации недостаточно, перед отправкой ее следует представить в удобном для пользователя формате, обычно в HTML. Для этого информация должна быть передана в одно из **представлений (View)**, которыми обычно являются JSP-страницы;
6. Последнее, что должен сделать контроллер, – упаковать вместе модель и имя представления для отображения результатов в браузере. Затем он отсылает запрос вместе с моделью и именем представления обратно входному контроллеру **DispatcherServlet**;
7. Чтобы контроллер не оказался тесно связанным с каким-либо конкретным представлением, имя представления, возвращаемое входному контроллеру **DispatcherServlet**, не определяет JSP-страницу непосредственно. Фактически оно даже не предполагает, что представление вообще является страницей JSP. Оно является лишь логическим именем представления, используемым затем для поиска фактического представления. Чтобы отобразить логическое имя представления в ссылку на конкретную реализацию, входной контроллер **DispatcherServlet** обратится к **арбитру представлений** (**view** **resolver**);
8. Теперь, когда контроллер **DispatcherServlet** определил, какое представление будет отображать результаты, работа запроса подошла к концу. Его конечная остановка – реализация представления (возможно, страница JSP), куда он доставит модель данных. На этом работа запроса заканчивается. На основе модели данных представление создаст отображение страницы, которое будет отправлено обратно клиенту с другим (не таким трудолюбивым) курьером – объектом ответа.

Рассмотренную выше схему работы фреймворка можно также представить следующей диаграммой

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25WbNsJWvvnW-hsqUs%2Fimage.png?alt=media\&token=959ce721-db15-450b-8e77-7ad78325d655)

## Создание и настройка Spring Boot проекта

Создадим новый Spring Boot проект, выберем следующие модули

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25WxnjgPlEY2x-YZ-L%2Fimage.png?alt=media\&token=7b5d48e0-b3bb-4d68-b27c-6876820a5783)

В новом проекте обратите внимание на структуру папок. В папке **resources\templates** будут содержаться html-файлы с использованием шаблонизатора **Thymeleaf**.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25X-5PIbgrM1bV5fRY%2Fimage.png?alt=media\&token=f45d6ce8-8cb4-4730-8205-00bf08ce5928)

Создадим файл **index.html**. Обратите внимание, что в теге html необходимо указать пространство имен **th** для подключения тегов **Thymeleaf**.

{% code title="index.html" %}
```markup
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
    <title>Index page</title>
</head>
<body>
    <h1>Hello, world!</h1>
</body>
</html>
```
{% endcode %}

В проекте Spring Boot MVC страница **index** будет автоматически передана при переходе на URL "/". Запустим проект и зайдем в браузер.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25X13GR5KYQS8DkfP6%2Fimage.png?alt=media\&token=e5c67628-b243-44c7-ac91-4fa903b5950d)

## Создание контроллера, переход между страницами

Создадим две html-страницы для нашего проекта.&#x20;

{% tabs %}
{% tab title="index.html" %}
{% code title="index.html" %}
```markup
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <title>Hello, world!</title>
</head>
<body class="bg-light">

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="#">Электронный деканат</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample05"
            aria-controls="navbarsExample05" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarsExample05">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item">
                <a class="nav-link" href="#">Главная</a>
            </li>
            <li class="nav-item active">
                <a class="nav-link" href="#">Студенты <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Группы</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Кафедры</a>
            </li>

        </ul>
        <form class="form-inline mt-2 mt-md-0">
            <input class="form-control mr-sm-2" type="text" placeholder="Введите текст" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Поиск</button>
        </form>
    </div>
</nav>

<div class="container">

    <div class="py-5 text-center">
        <h2>Управление студентами</h2>
        <p class="lead">На данной странице вы можете добавить, отредактировать поля или удалить студентов кафедры</p>
    </div>


    <div class="row">
        <div class="col">
            <h2>Группа АИ-171</h2>
        </div>
        <div class="col col-lg-3">
            <a th:href="@{~/add_student}" class="btn btn-lg btn-block btn-outline-primary" role="button"
               aria-disabled="true">Добавить студента</a>
        </div>
    </div>

    <br/>

    <div class="table-responsive">
        <table class="table table-striped">
            <thead>
            <tr>
                <th class="text-justify">#</th>
                <th class="text-justify">Фамилия</th>
                <th class="text-justify">Имя</th>
                <th class="text-justify">Отчество</th>
                <th class="text-justify">Почта</th>
                <th class="text-justify">Телефон</th>
                <th class="text-justify">Адрес</th>


            </tr>
            </thead>
            <tbody>

            <tr th:each="student : ${students}">
                <td class="align-middle"><span th:text="${student.id}"/></td>
                <td class="align-middle"><span th:text="${student.lastName}"/></td>
                <td class="align-middle"><span th:text="${student.firstName}"/></td>
                <td class="align-middle"><span th:text="${student.patronymic}"/></td>
                <td class="align-middle"><span th:text="${student.email}"/></td>
                <td class="align-middle"><span th:text="${student.phone}"/></td>
                <td class="align-middle"><span th:text="${student.address}"/></td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
        integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
        crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        crossorigin="anonymous"></script>
</body>
</html>
```
{% endcode %}
{% endtab %}

{% tab title="add_student.html" %}
{% code title="add_student.html" %}
```markup
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <title>Добавление студента</title>
</head>
<body class="bg-light">
<div class="container">
    <div class="py-5 text-center">
        <h2>Добавление студента</h2>
        <p class="lead">Заполните поля и нажмите кнопку 'Добавить студента'</p>
    </div>

    <form method="post" class="needs-validation" novalidate>
        <div class="row">
            <div class="col-md-12">
                <h4 class="mb-3">Поля для заполнения</h4>

                <div class="row">

                    <div class="col-md-4 mb-3">
                        <label for="lastName">Фамилия</label>
                        <input type="text" class="form-control" id="lastName" placeholder=""
                               value="" required>
                    </div>


                    <div class="col-md-4 mb-3">
                        <label for="firstName">Имя</label>
                        <input type="text" class="form-control" id="firstName" placeholder=""
                               value="" required>
                    </div>
                    <div class="col-md-4 mb-3">
                        <label for="patronymic">Отчество</label>
                        <input type="text" class="form-control" id="patronymic" placeholder=""
                               value="" required>
                    </div>
                </div>

                <div class="mb-3">
                    <label for="email">Электронная почта</label>
                    <input type="email" class="form-control" id="email"
                           placeholder="" value="" required>
                </div>

                <div class="mb-3">
                    <label for="phone">Телефон</label>
                    <input type="text" class="form-control" id="phone"
                           placeholder="(ХХХ) ХХХ-ХХХХ" value="" required>
                </div>

                <div class="mb-3">
                    <label for="address">Домашний адрес</label>
                    <input type="text" class="form-control" id="address" placeholder="" value=""
                           required>
                </div>

                <hr class="mb-4">
                <button class="btn btn-primary btn-lg btn-block" type="submit" value="Submit">Добавить студента</button>
    </form>
</div>
</div>
</div>

<br/><br/><br/>


<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
        integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
        crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        crossorigin="anonymous"></script>
</body>
</html>
```
{% endcode %}
{% endtab %}
{% endtabs %}

Создадим класс контроллера, который обрабатывает GET запросы с URL **"/"** и **"/add\_student"**.

{% code title="StudentController.java" %}
```java
@Controller
public class StudentController {

    @GetMapping("/")
    public String index(Model model) {
        return "index";
    }

    @GetMapping("/add_student")
    public String addStudent(Model model) {
        return "add_student";
    }
}
```
{% endcode %}

В файле **index.html** добавим ссылку для кнопки "Добавить студента" . Для формирования ссылки используем тег **th:href**. Для указания пути относительно домена используем комбинацию **@{\~}**.

{% code title="index.html" %}
```markup
        <div class="col col-lg-3">
            <a th:href="@{~/add_student}" class="btn btn-lg btn-block btn-outline-primary" role="button"
               aria-disabled="true">Добавить студента</a>
        </div>
```
{% endcode %}

Проверим работу приложения в браузере

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25XlUMW-6ZT1XiXku5%2Fimage.png?alt=media\&token=aea76020-f0ee-4f63-9b53-4f18695feb89)

Нажмем на кнопку "**Добавить студента**"

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-M25V1nXgi3RexFLNnne%2F-M25Xyr9QyrdHEZYOX4K%2Fimage.png?alt=media\&token=88ba6a2d-83ed-45bf-8ba4-659734a4a0ff)
