Лекция 7-8

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

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

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

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

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

Краткое описание схемы работы 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), куда он доставит модель данных. На этом работа запроса заканчивается. На основе модели данных представление создаст отображение страницы, которое будет отправлено обратно клиенту с другим (не таким трудолюбивым) курьером – объектом ответа.

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

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

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

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

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

index.html
<!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>

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

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

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

index.html
<!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>

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

StudentController.java
@Controller
public class StudentController {

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

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

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

index.html
        <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>

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

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

Last updated