从去年12月份写好自己的第一个正式项目,到现在3个月没有碰过Django了,今天终于又回到了Web开发的地方。开始学习Spring MVC。

Spring MVC是一个Java Web开发的框架,基于Model-View-Contorller设计模式,同时基于Spring框架的IOC,DI等特性上。

Spring MVC的好处有:

  1. 采用Spring的方式创建Web应用和UI
  2. 创建一系列可以反复使用的UI组件
  3. 创建一系列可以反复使用的UI组件
  4. 对表单数据进行便捷的操作
  5. 视图层的灵活配置与第三方模板兼容

Spring MVC的最新文档在https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html

一般一个Spring MVC Web应用包含如下部分:

  1. 一些Web组件和其他用于展示的内容
  2. 各种不同功能的Spring Bean
  3. Spring的配置

Spring MVC Web应用的一些基础概念

一个Spring MVC的Web应用通常流程是这样的:

  1. 浏览器发来请求
  2. 请求被HTTP代理交给Front Controller
  3. Front Controller 交给 Controller
  4. Controller 处理完毕,交给View Template生成响应
  5. 响应交给HTTP代理返回给用户
  6. 中间利用到数据库数据的时候,Controller通过Model来使用数据

在Spring MVC里,我们把Front Controller称作DispatcherServlet

DispatcherServlet是Spring框架的一部分,已经被Spring开发小组编写好,所以不用编写这部分的代码。

对于我们Web开发,我们需要开发的是Controller,View templates和Model这三部分,也就是MVC设计模式的三部分。

Controller是业务逻辑代码,一般用于处理请求,存取和处理数据,与模型进行数据交互,经过Controller处理的数据,会被发送到View Template。

Model是数据容器,现在常用ORM来处理数据库,得到一个数据对象,交给Controller进行处理。模型从后台数据系统中获取数据,并被抽象成对象,比如Java中的Bean。

View Template顾名思义就是模板,一种将静态模板结合动态数据,最终生成动态响应数据的地方,用处就是展示Controller和Model处理后的结果,一般使用JSP+JSTL技术,Spring MVC也支持很多其他类型的模板比如Thymeleaf, Groovy, Veolocity, Freemarker等等。

在官方文档中都可以找到针对这些内容的详细说明。

创建Spring MVC Web应用结构

其实就是创建一个Web应用,然后把Spring的包作为lib添加到Web-INF目录下的lib中,由于后边要用到JSTL,所以把JSTL的两个库文件也一并添加。然后需要进行一系列配置:

  1. 在WEB-INF/web.xml中配置DispatcherServlet:

    <?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">
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>WEB-INF/spring-mvc-demo-servlet.xml</param-value>
            </init-param>
            
            <load-on-startup>1</load-on-startup>
        </servlet>
    </web-app>

    这里配置了一个Servlet的名字是dispatcher(可以任意起),具体的Servelet类是Spring框架中编写好的DispatcherServlet。
    然后还配置了一个初始化参数,参数名必须是contextConfigLocation,然后指定了Spring mvc配置文件的路径。

  2. 设置DispatcherServlet的URL mapping:

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    有Java EE开发经验的都知道,不配置mapping就会报错,这里我们使用反斜杠,让所有的URL请求,都交给DispatcherServlet进行处理。
    此时启动服务器,会报404错误。

  3. 配置WEB-INF/spring-mvc-demo-servlet.xml(名字可以任意起,是一个针对Spring MVC的配置文件)。
    在web.xml的同级目录创建一个符合要求的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"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="
    		http://www.springframework.org/schema/beans
        	http://www.springframework.org/schema/beans/spring-beans.xsd
        	http://www.springframework.org/schema/context
        	http://www.springframework.org/schema/context/spring-context.xsd
        	http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    </beans>
  4. 启用对Spring组件扫描的支持,在beans标签内添加:

    <context:component-scan base-package="mvcbeans" />

    这就是XML配置中启用组件扫描的配置,包名可以是自己在src目录下创建任意名称的包,这是为了让Spring获取Bean。

  5. 启用对于表单数据转换,格式化和有效性的支持,同样在beans标签内添加:

    <mvc:annotation-driven/>

    这个现在还不怎么明白,先配置好再说

  6. 配置Spring MVC View Resolver,还是在beans标签内:

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/" />
        <property name="suffix" value=".jsp" />
    </bean>

    这个视图解析器的意思是,使用InternalResourceViewResolver这个类解析模板,然后按照前缀+名称+后缀名的方式去寻找对应的视图文件。如果提供了一个名称是students的视图名称,则实际寻找的名称是/WEB-INF/view/students.jsp

此时启动服务器,Tomcat能够正常启动,但是日志中会提示org.springframework.web.servlet.DispatcherServlet noHandlerFound,这是因为前端控制器找不到具体处理请求的控制器,下边需要编写一个最简单的控制器和视图。

编写第一个控制器与视图

DispatcherServlet的任务是将请求发送给Spring MVC 控制器(controller),控制器是用于处理请求并且返回一个视图名称的Spring组件,由于一般Web应用的控制器有很多,所以DispatcherServlet需要一个处理器映射(Handler Mapping)来寻找对应的控制器,这也就是前边提示noHandlerFound的原因。

编写控制器的步骤是:

  1. 创建控制器类
  2. 创建控制器方法
  3. 把Controller方法与请求的URL进行匹配
  4. 返回视图的名称

第一步,需要编写一个控制器类,然后用@Controller进行注解,@Controller继承了@Component注解,用来标示一个控制器,同时告诉Spring将其装配成一个Bean。

第二步和第三步联系比较紧密,在控制器中编写一个自定义的方法,参数可以随意,然后用@RequestMapping("URL")进行注解,这个注解告诉这个方法用于处理哪个URL发来的请求。

第四步,无论方法内部进行了什么操作,需要返回一个字符串值,Spring MVC会将这个字符串值当做视图的名称,经过视图解析器处理成完整视图文件路径,然后去寻找这个视图文件。

我们来为我们的第一个应用编写一个对应”/”的视图控制器,在src目录下的mvcbeans包里创建一个HomeController.java:

package mvcbeans;

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

@Controller
public class HomeController {

    @RequestMapping("/")
    public String showHomePage() {
        return "home-page";
    }
}

这里首先用@Controller注解了这个类,让其被Spring组装成一个Bean并且作为一个控制器注册,然后使用@RequestMapping(“/”),让其中的方法showHomePage()对应”/”路径的操作,最后返回一个字符串”home-page”,这个字符串会被View Resolver解析成/WEB_INF/view/home-page.jsp。

还要按照视图解析器的路径,在/WEB-INF/view/下边来创建这个jsp文件:

//home-page.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
    <title>FirstSpringMVC</title>
</head>
<body>
<h1 style="text-align: center;">第一个Spring MVC Web应用</h1>
</body>
</html>

然后启动项目,访问localhost:8080/,成功的启动了第一个Spring MVC Web应用!

注意,实际的URL访问路径不一定是localhost:8080/,在Intellij中可以在Run–Edit Configurations–Tomcat Server–Deployment–Application Context中修改Web项目上下文。我这里将Application Context清空了,所以直接就是根路径。

简单回顾一下第一个Spring MVC Web应用

第一个应用我们是采取的以web.xml为入口的配置(当然还会有其他不通过web.xml的配置,但是目前这一种最易于理解)。

配置并且运行的实际过程是:

  1. 在web.xml中配置来自Spring MVC的前端控制器负责所有Web应用请求的分发
  2. 在web.xml的init-param中配置了contextConfigLocation以及对应的配置文件的路径
  3. 前端控制器在这里实际上创建了Spring容器,并且按照配置文件里的路径去组装了Bean
  4. Web请求被前端控制器交给处理”/”路径的控制器
  5. 控制器完成工作(实际上现在的控制器什么也没做),返回了一个字符串
  6. 字符串被交给解析器,解析器根据配置文件中的前后缀拼成一个视图路径
  7. 视图路径中的内容被组装成请求返回给浏览器

跑起来了第一个Web应用,就可以跑起来无数个更复杂的Web应用,现在只用了视图和控制器,还没有用到模型。Web开发的后续,肯定是先来增删改查啦。