第一个Spring MVC程序

 
本节,我们将通过一个简单的 Java Web 应用来演示下如何创建 Spring MVC 程序。

创建 Java Web 项目

首先,我们需要使用 Eclipse 创建一个 Java Web 项目,具体步骤如下。

1. 在 Eclipse 中,依次单击 File -> New -> Dynamic Web Project,创建一个 Java Web 项目,如下图所示。

图1:创建一个 Java Web 项目

2. 在弹出的 Dynamic Web Project 窗口中,设置 Project Name 为 first-springmvc-demo,然后点击下方的 New Runtime... 按钮,对该项目运行所需的服务器进行设置,如下图。


图2:设置项目名称和运行时环境

3. 在弹出的 New Server Runtime Environment 窗口中,选择 Apache 下对应版本的 Tomcat(这里我们以 Tomcat v9.0 为例),然后点击下方的 Next 按钮,如下图。


图3:选择 Tomcat 服务器版本

4. 在 Tomcat Server 窗口中,设置本地 Tomcat 的路径以及 JRE 版本,如下图所示。


图4:设置 Tomcat 路径

5. 返回 Dynamic Web Project 窗口,可以看到 Tomcat 服务器已经设置完成,点击下方的 Next 按钮,如下图所示。


图5:Web 项目设置

6. 在 Web Module 窗口中将 Content directory 修改为 webapp,并勾选下方的 Gnerate web.xml deployment desciptor,最后点击下方的 Finish 按钮完成项目的创建,如下图所示。


图6:修改 Content 目录,生成 web.xml

7. Java Web 项目  first-springmvc-demo 创建完成之后,结构如下图所示。


图7:项目结构
自此,我们就完成了使用 Eclipse 创建 Java Web 项目的全部步骤。

导入 Spring MVC 相关依赖

在完成了 Java Web 项目的创建后,接下来,我们需要将 Spring MVC 相关的依赖引入到该项目中。将以下依赖 Jar 包导入到 first-springmvc-demo 项目的 webapp/WEB-INF/lib 目录中。
  • commons-logging-1.2.jar
  • spring-aop-5.3.13.jar
  • spring-beans-5.3.13.jar
  • spring-context-5.3.13.jar
  • spring-core-5.3.13.jar
  • spring-expression-5.3.13.jar
  • spring-web-5.3.13.jar
  • spring-webmvc-5.3.13.jar

其中 spring-web-5.3.13.jar 和 spring-webmvc-5.3.13.jar 是 Spring MVC 的依赖包,其余的则都是 Spring 的核心依赖。关于 Spring 这些依赖包的下载,请参考《Spring 开发环境搭建》。

导入 Thymeleaf 相关依赖

对于 Web 项目而言,前后端分离可以说是当今最主流的思想,该思想概括成一句话就是“前端只写前端的代码,后端只写后端的代码,前后端互不影响”。

在传统的 Java Web 开发中,通常都是使用 JSP 来进行前台页面的开发。但 JSP 页面中常常会掺杂这一些后端的 Java 代码,其中任何一个简单的 Java 代码问题都有可能导致整个项目无法启动,而这显然不符合“前后端分离”的思想。

因此在现在的 Java Web 开发中,已经很少有人在使用 JSP 了。各互联网公司、组织或个人都陆续使用其他技术来代替 JSP ,而 Thymeleaf 就是其中较为优秀的技术之一。

Thymeleaf 简介

Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等一样,也可以轻易地与 Spring MVC 等 Web 框架集成。但与其它模板引擎相比,Thymeleaf 最大的特点就是,即使不启动 Web 应用也可以直接在浏览器中打开并正确地显示页面。

注意:本套教程中,所有案例的页面都是使用 Thymeleaf 编写的,Thymeleaf 的用法也比较简单,详细介绍请参考《Thymeleaf教程(10分钟入门)》。

导入 Thymeleaf 依赖

想要使用 Thymeleaf 进行前台页面开发,必不可少的一步就是将 Thymeleaf 的相关依赖引入到项目中。

将以下依赖存放到 first-springmvc-demo 项目的 webapp/WEB-INF/lib 目录中,以完成 Thymeleaf 依赖的导入。

注:点击以上依赖,即可下载对应依赖的 Jar 包。

配置 DispatcherServlet(前端控制器)

Spring MVC 是基于 Servlet 的,DispatcherServlet 是整个 Spring MVC 框架的核心,主要负责截获请求并将其分派给相应的处理器处理。

跟所有的 Servlet 一样,DispatcherServlet 也需要在 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">

    <!-- 配置 SpringMVC 的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--设置springMVC的核心控制器所能处理的请求的请求路径
        /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
        但是/不能匹配.jsp请求路径的请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Spring MVC 初始化时,会自动从应用程序的 WEB-INF 目录下查找 Spring MVC 的配置文件。该配置文件的默认命名规则为 {servlet-name}-servlet.xml,例如 springMVC-servlet.xml。

我们也可以将 Spring MVC 的配置文件存放在应用程序目录中的其他位置,但需要在 <servlet> 元素下通过 <init-param> 元素的 contextConfigLocation 参数,来指定 Spring MVC 配置文件的位置,示例代码如下。
<?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">

    <!-- 配置 SpringMVC 的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置 DispatcherServlet 的一个初始化参数:spring mvc 配置文件按的位置和名称-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>

        <!--作为框架的核心组件,在启动过程中有大量的初始化操作要做
            而这些操作放在第一次请求时才执行会严重影响访问速度
            因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--设置springMVC的核心控制器所能处理的请求的请求路径
        /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
        但是/不能匹配.jsp请求路径的请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

默认情况下,所有的 Servlet(包括 DispatcherServlet )都是在第一次调用时才会被加载。这种机制虽然能一定程度上降低项目启动的时间,但却增加了用户第一次访问所需的时间,给用户带来不佳的体验。因此在上面的 web.xml 中,我们通过 <load-on-startup> 标签对 Spring MVC 前端控制器 DispatcherServlet 的初始化时间进行了设置,让它在项目启动时就完成了加载。

load-on-startup 元素取值规则如下:
  • 它的取值必须是一个整数;
  • 当值小于 0 或者没有指定时,则表示容器在该 Servlet 被首次请求时才会被加载;
  • 当值大于 0 或等于 0 时,表示容器在启动时就加载并初始化该 Servlet,取值越小,优先级越高;
  • 当取值相同时,容器就会自行选择顺序进行加载。

此外,我们还通过 <servlet-mapping> 将 DispatcherServlet 映射到“/”,表示 DispatcherServlet 需要截获并处理该项目的所有 URL 请求(以 .jsp 为后缀的请求除外)。

创建 Spring MVC 配置

在 first-springmvc-demo 项目的 src 目录下,创建一个名为 springMVC.xml 的配置文件,配置内容如下。
<?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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="net.biancheng.c"></context:component-scan>

    <!-- 配置 Thymeleaf 视图解析器 -->
    <bean id="viewResolver"
          class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

在上面的配置中,我们定义一个名为 viewResolver,类型为 ThymeleafViewResolver 的 Bean,这其实是 Thymeleaf 的视图解析器。通过它,我们可以对 Thymeleaf 视图的编码、视图前缀、视图后缀等进行配置。

创建 Controller(控制器)

我们知道,DispatcherServlet 会拦截用户发送来的所有请求进行统一处理,但不同的请求有着不同的处理过程,例如登陆请求和注册请求就分别对应着登陆过程和注册过程,因此我们需要 Controller 来对不同的请求进行不同的处理。

在 Spring MVC 中,一个普通的 Java 类只要标注了 @Controller 注解,就会被 Spring MVC 识别成 Controller。Controller 类中的每一个处理请求的方法被称为“控制器方法”。

控制器方法在处理完请求后,通常会返回一个字符串类型的逻辑视图名(ViewName),Spring MVC 需要借助 ViewResolver(视图解析器)将这个逻辑视图名解析为真正的 View 视图,最终响应给客户端展示。

例如,在 first-springmvc-demo 项目的 src 目录下创建一个 net.biancheng.c.controller 包,并在该包下创建一个名为 HelloController 的控制器类,代码如下。
package net.biancheng.c.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {

    @RequestMapping("/")
    public String sayHello() {
        //视图名,视图为:视图前缀+index+视图后缀,即 /WEB-INF/template/index.html
        return "index";
    }

    @RequestMapping("/login")
    public String welcome() {
        //视图名,视图为:视图前缀+login+视图后缀,即 /WEB-INF/template/login.html
        return "login";
    }

    @RequestMapping("/register")
    public String success() {
        //视图名,视图为:视图前缀+register+视图后缀,即 /WEB-INF/template/register.html
        return "register";
    }
}

在以上代码中,除了 @Controller 注解外,我们还在方法上使用了 @RequestMapping 注解,它的作用就是将请求和处理请求的控制器方法关联映射起来,建立映射关系。Spring MVC 的 DispatcherServelt 在拦截到指定的请求后,就会根据这个映射关系将请求分发给指定的控制器方法进行处理。

创建 View(视图)

根据 Spring MVC 配置文件中关于 Thymeleaf 视图解析器的配置可知,所有的 Thymeleaf 视图文件都应该存放在 /WEB-INFO/temlpates 目录下且文件名必须以 .html 结尾。

1. 在 /WEB-INFO 目录下创建一个 templates 的目录,并在该目录下创建 index.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>C语言中文网</title>
</head>
<body>
<h1 th:text="欢迎您访问C语言中文网"></h1>

未注册用户,请<a th:href="@{/register}">注册</a><br/>
已注册用于,去<a th:href="@{/login}">登陆</a>
</body>
</html>

2. 在 /WEB-INFO/templates 下创建 login.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>C语言中文网</title>
</head>
<body>
<h1 th:text="欢迎来登陆页面"></h1>
<a th:href="@{/}">跳转首页</a><br/>
<a th:href="@{/register}">跳转到注册页面</a>
</body>
</html>

3. 在 /WEB-INFO/templates 下创建 register.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>C语言中文网</title>
</head>
<body>
<h1 th:text="欢迎来到注册页面"></h1>
<a th:href="@{/}">跳转首页</a><br/>
<a th:href="@{/login}">跳转到登陆页面</a>
</body>
</html>

部署运行

1. 将 first-springmvc-demo 部署到 Tomcat 中,启动 Tomcat 服务器,使用浏览器访问“http://localhost:8080/first-springmvc-demo/”,结果如下图。


图8:首页

2. 点击注册,跳转到注册页面,结果如下图。


图9:注册页面

3. 点击登陆,跳转到登陆页面,结果如下图。


图10:登陆页面

总结

通过这个案例,我们可以总结出 Spring MVC 主要的执行流程:
  1. 浏览器发送一个请求,若请求地址与 web.xml 中配置的前端控制器(DispatcherServlet)的 url-pattern 相匹配,则该请求就会被前端控制器 DispatcherServlet 拦截;
  2. 前端控制器(DispatcherServlet )会读取 SpringMVC 的核心配置文件,通过组件扫描获取到所有的控制器(Contorller);
  3. 将请求信息和控制器中所有控制器方法标识的 @RequestMapping 注解的 value、method 等属性值进行匹配。若匹配成功,则将请求交给对应的 @RequestMapping 注解所标识的控制器方法处理;
  4. 处理请求的方法会返回一个字符串类型的视图名称,该视图名称会被 Spring MVC 配置文件中配置的视图解析器(ViewResolver)解析真正的视图(View)对象,最终展示给客户端。