Статьи

Spring MVC Tutorial

1. Введение

Как Enterprise Java Developer, одним из основных направлений работы является разработка веб-приложений. С веб-приложением многие проблемы также входят в картину как последствия. Точнее говоря, некоторые из них — это управление состоянием, рабочий процесс и проверки. Отсутствие состояния протокола HTTP только усложняет ситуацию. Веб-фреймворк Spring разработан, чтобы помочь нам в решении этих проблем. Основываясь на шаблоне Model-View-Controller (MVC), Spring MVC помогает нам создавать приложения, которые очень слабо связаны и гибки, даже после того, как мы позаботимся обо всех проблемах, связанных с протоколом HTTP и другими.

В этом уроке мы начнем с понимания назначения каждого компонента Spring MVC и роли, которую они играют в обработке объектов запросов и ответов на протяжении всей своей жизни в приложении Spring. После этого мы начнем настраивать простое приложение Spring MVC, добавляя компонент на каждом шаге, писать очень простой шаблон Thymeleaf и, наконец, включать тестовый пример JUnit для написанного нами контроллера. Давайте начнем!

2. Жизнь запроса

Жизнь объекта запроса

Жизнь объекта запроса

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

Давайте разберемся, какие шаги выполняет объект запроса в приложении Spring MVC:

  1. Сначала запрос поступает в DispatcherServlet который действует как Front Controller и делегирует ответственность другим компонентам платформы для выполнения обработки. Чтобы определить контроллер, куда должен быть отправлен запрос, DispatcherServlet обращается к сопоставлениям обработчиков, которые извлекают URL-адрес из объекта запроса и находят соответствующий контроллер для этого запроса.
  2. Как только запрос достигает Контроллера, он отбрасывает всю имеющуюся у него информацию и терпеливо ждет, пока Контроллер выполнит свою обработку. Обычно Controller предоставляет модель и имя представления, которое предоставляет HTML-контент для ответа.
  3. Чтобы сохранить связь между Контроллером и представлениями, Контроллер возвращает имя логического представления в виде строки в DispatcherServlet . Затем DispatcherServlet обращается к View Resolver для сопоставления этого имени логического представления String с конкретной реализацией представления, которая может быть страницей JSP или страницей HTML.
  4. После того, как DispatcherServlet знает, какое представление визуализировать, он, наконец, отправляет данные модели в фактическое представление, чтобы можно было создать объект ответа. Теперь срок действия объекта запроса окончен.
  5. Теперь это представление преобразуется в объект ответа и отправляется обратно пользователю, где его можно отобразить в браузере.

Похоже, что вся работа была выполнена объектом запроса, и пользователь видит только объект ответа. Это правда!

3. Настройка проекта

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

Создать проект

1
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-SpringMVC-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

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

Создание проекта с Maven

Создание проекта с Maven

Создав проект, не стесняйтесь открывать его в своей любимой среде IDE. Следующим шагом является добавление соответствующих зависимостей Maven в проект.

Вот файл pom.xml с соответствующими зависимостями:

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.javacodegeeks.example</groupId>
  <artifactId>JCG-SpringMVC-example</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>JCG-SpringMVC-example</name>
  <packaging>war</packaging>
  <description>Spring MVC Hello World Example</description>
 
  <dependencies>
 
    <!--Spring MVC Dependencies-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
 
    <!-- Servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
 
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
        </configuration>
      </plugin>
    </plugins>
    <!-- added to remove Version from WAR file -->
    <finalName>${project.artifactId}</finalName>
  </build>
</project>

Наконец, чтобы понять все JAR-файлы, которые добавляются в проект при добавлении этой зависимости, мы можем запустить простую команду Maven, которая позволяет нам видеть полное дерево зависимостей для проекта, когда мы добавляем в него некоторые зависимости. Вот команда, которую мы можем использовать:

Дерево зависимостей

1
mvn dependency:tree

Когда мы запустим эту команду, она покажет нам следующее дерево зависимостей:

Дерево зависимостей проекта Spring MVC

Дерево зависимостей проекта Spring MVC

4. Настройка DispatcherServlet

Как мы уже говорили в предыдущем разделе, все начинается с DispatcherServlet . Итак, это будет первый компонент, который мы добавим в наш проект. Обратите внимание, что вместо файла web.xml мы настроим DispatcherServlet в классе Java:

AppInitializer.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.javacodegeeks.example.config;
 
import org.springframework.web.servlet.support.
        AbstractAnnotationConfigDispatcherServletInitializer;
 
public class AppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {
 
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }
 
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }
 
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

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

Метод getServletMappings() определяет путь, по которому будет сопоставлен DispatcherServlet . Здесь, как мы указали / , это будет сервлет приложения по умолчанию. Когда DispatcherServlet запускается, он создает контекст приложения Spring и начинает загружать его с bean-компонентами, объявленными в классе WebConfig который определяется методом getServletConfigClasses() . Наконец, классы, возвращаемые методом getRootConfigClasses() используются для загрузки контекста, созданного ContextLoaderlistener .

Давайте теперь определим классы WebConfig и RootConfig и посмотрим, каковы их функции.

5. Включение Web MVC

Одним из классических способов включения компонентов Spring MVC является использование файлов XML с элементы. Но на этом уроке мы покажем новейший способ предоставления всей конфигурации с использованием только классов Java. Давайте включим Spring MVC Components, определив наш класс WebConfig :

WebConfig.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.javacodegeeks.example.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
 
@Configuration
@EnableWebMvc
@ComponentScan("com.javacodegeeks.example")
public class WebConfig extends WebMvcConfigurerAdapter {
     
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver =
                new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setExposeContextBeansAsAttributes(true);
        return viewResolver;
    }
 
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Многое произошло в этом классе конфигурации. Давайте разберемся с каждой из этих вещей:

  • Первое, что мы сделали, это аннотировали класс с помощью @ComponentScan . Это сообщает Spring, какой пакет сканировать, чтобы найти Spring Beans и компоненты, которые мы определяем. Он также проанализирует все дочерние пакеты в упомянутом пакете.
  • Мы также определяем ViewResolver компонент ViewResolver для класса и настраиваем его на поиск страниц JSP в указанном месте относительно пути к классам для приложения.
  • Наконец, этот класс расширяет WebMvcConfigurerAdapter и переопределяет метод configureDefaultServletHandling и вызывает enable() для объекта configurer, который сообщает DispatcherServlet пересылать запрос статических ресурсов в ViewResolver и не обрабатывает их самостоятельно.

6. Определение Root Config

Для компонентов контекста приложения в ContextLoaderlistener мы не будем определять какие-либо bean-компоненты. В классе RootConfig мы можем даже определить компоненты для не-веб-компонентов. Давайте посмотрим на пример кода:

RootConfig.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.javacodegeeks.example.config;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
@Configuration
@ComponentScan(basePackages = {"com.javacodegeeks.example"},
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
        })
public class RootConfig {
}

Единственное, что мы сделали, это аннотировали класс с помощью @ComponentScan . Это сообщает Spring, какой пакет сканировать, чтобы найти не-веб-компоненты, которые могут существовать в проекте.

7. Написание простого контроллера

Все становится интересным, когда мы определяем компонент, который позволяет конечному пользователю взаимодействовать с приложениями, которые мы создаем. Контроллер — это компонент, который обеспечивает отображение в форме URL-адресов, с которыми пользователь может взаимодействовать, чтобы получить какой-либо ответ. В этом примере мы создадим одно сопоставление запроса GET, которое просто возвращает логическое имя представления, которое затем разрешается в JSP с помощью созданного нами средства просмотра разрешения. Давайте посмотрим на пример контроллера, который мы создали:

HomeController.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.javacodegeeks.example.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
public class HomeController {
 
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "home";
    }
}

Мы определили здесь одно GET Mapping, которое просто возвращает имя логического представления, т.е. home . Это будет разрешено по пути /WebContent/WEB-INF/views/home.jsp .

8. Проектирование Представления

Для представления JSP мы спроектируем очень простую страницу JSP, которая просто представит пример дизайна. Мы создали этот файл из корня приложения и по пути /WebContent/WEB-INF/views/home.jsp . Вот файл:

home.jsp

01
02
03
04
05
06
07
08
09
10
11
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
    <head>
        <title>JavaCodeGeeks: Spring MVC</title>
    </head>
    <body>
        <h1>Welcome Spring MVC</h1>
        <p>Hello JavaCodeGeeks!</p>
    </body>
</html>

Чтобы увидеть, что мы сделали, мы запустим наше приложение дальше. Обратите внимание, что это просто базовая страница JSP, и ничего больше не нужно представлять, как ViewResolver имя логического представления в конкретное представление, которое DispatcherServlet возвращает в качестве ответа.

9. Запуск приложения

Пришло время запустить наше приложение. Мы используем IntelliJ IDE для запуска проекта. Чтобы запустить проект, сначала настройте Tomcat для принятия разнесенного артефакта WAR:

Конфигурация Tomcat с разнесенной WAR

Конфигурация Tomcat с разнесенной WAR

Наконец, когда мы запускаем наше приложение, мы можем видеть вывод из созданного нами представления JSP:

Запуск Spring MVC Application

Запуск Spring MVC Application

10. Spring MVC с Spring Boot

Spring Boot — отличная платформа, которая построена на основе Spring MVC Framework и делает все намного быстрее и проще. Эта простота достигается благодаря использованию множества стандартных настроек и конфигураций, связанных с зависимостями, которые мы добавляем в наш проект. Другое преимущество, которое предлагает Spring Boot, заключается в том, что он позволяет нам определять родительскую зависимость, посредством которой поддерживается совместимость версий по всему проекту. Давайте посмотрим на это подробно.

10.1 Стартеры весенней загрузки

Spring Boot предоставляет очень удобные начальные зависимости, которые позволяют нам выбрать функциональность и добавить связанную с ней зависимость в наш проект. Как только это будет сделано, Spring Boot сам управляет и вносит все связанные зависимости в наш проект с правильным номером версии, гарантируя, что эти зависимости будут совместимы друг с другом. Родительская зависимость определяется как:

Зависимость стартера

1
2
3
4
5
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.RELEASE</version>
</parent>

Если вы предоставите здесь version и groupId использовать тот же groupId в разделе dependencies в файле pom.xml , версия будет автоматически импортирована.

10.2. Просмотр приложений Spring Boot

Каждому приложению Spring Boot предоставляется точка входа в форме одного и того же старого метода main() . Мы можем запустить этот метод, как обычный основной метод, который заботится об инициализации Spring Beans и запуске приложения. Наиболее распространенная форма основного класса Spring Boot может быть показана здесь:

Spring Boot Основной класс

1
2
3
4
5
6
@SpringBootApplication
public class JCGApplication {
    public static void main(String[] args) {
        SpringApplication.run(JCGApplication.class, args);
    }
}

С помощью одной аннотации @SpringBootApplication мы включили следующие аннотации:

  • @Configuration : эта аннотация помечает этот класс как класс Configuration и инициализируется сначала Spring Container
  • @EnableAutoConfiguration : эта аннотация подразумевает, что каждый класс, помеченный аннотациями, такими как @Component и связанные, будет загружен как Spring Bean
  • @ComponentScan : Наконец, эта аннотация автоматически включит сканирование компонентов. По умолчанию, когда мы не предоставляем никакого значения пакета, эта аннотация выберет пакет, в котором находится этот класс, и начнет оттуда искать компоненты Spring.

Посмотрите, как легко сделать другие вещи с Spring Boot здесь .

11. Заключение

На этом уроке мы поняли, что происходит, когда объект запроса покидает веб-браузер, и как он преобразуется в объект ответа. В этом посте также объясняется, какую важную роль играет DispatcherServlet при обработке запроса от клиента. Мы также сделали простое приложение Spring MVC, чтобы увидеть, как имена представлений также преобразуются в конкретные реализации представлений.

Важно понимать, что хотя Spring Boot превратил Spring в гораздо более простое рабочее пространство, он по-прежнему полагается на Spring MVC для обеспечения настроек по умолчанию и компонентов.

12. Загрузите исходный код

Это был пример простого приложения Spring MVC.

Скачать
Вы можете скачать полный исходный код этого примера здесь: JCG-SpringMVC-example