基于最新Spring 5.x,详细介绍了MVC架构以及Spring MVC入门案例的搭建。

此前,我们已经学习过了传统Servlet编程,现在我们来学习基于Servlet的更加上层的Spring MVC编程。

Spring Web MVC是构建在原始的Servlet API 上的Web 框架,并且从一开始就包含在 Spring Framework中,是Spring的核心组件。它正式名称"Spring Web MVC"是来自Spring的源码模块(spring-webmvc,https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc)的名称,但它更通常被称简称为"Spring MVC"。

也就是说,Spring MVC框架本身就是基于Servlet规范的,但是它对原始的Servlet API进行了封装,屏蔽了底层原始的Servlet方法,提供了更加高级的开发模式和注解的支持,用于方便开发者快速开发基于Servlet API并部署到Servlet容器的Web应用程序。

下面我们学习Spring MVC,和Spring的学习时一样,我们从基于XML的配置入手,逐渐过渡到基于JavaCofig的配置,从而去除XML文件,在此后我们还会学习Spring Boot,那时候使用Spring MVC就更加简单了。

文章目录

  • Spring MVC学习 系列文章
  • 1 MVC和三层架构
  • 2 Spring MVC的入门案例
  • 2.1 项目搭建
  • 2.2 maven依赖
  • 2.3 添加Spring MVC配置文件
  • 2.4 编写view视图文件
  • 2.5 配置DispatcherServlet
  • 2.6 编写Controller控制器
  • 2.7 启动测试
  • 2.8 简单的执行流程
  • 2.9 基于JavaConfig的配置


1 MVC和三层架构

Spring MVC基于MVC设计模式。在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使用的非常多,属于宏观的解决方案。而MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式:

  1. 表现层,框架有比如SpringMVC和struts2。
  1. 也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用HTTP协议请求web层,web需要接收HTTP请求,完成HTTP响应。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将业务层返回的处理结果响应给客户端。
  2. 表现层的设计一般都使用MVC模型。(MVC是表现层的设计模型,和其他层没有关系):
  1. V即View视图,是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。视图层仅仅是展示数据,并且提供给用户的对应的操作界面。位于最上层。
  2. C即Controller控制器,是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个model模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。简单的说,Controller负责转发请求和响应,它将View和Model联系起来。Controller位于第二层。
  3. M即Model模型,包括业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现。在MVC的三个部件中,模型拥有最多的处理任务,Model位于最下层。
  1. 表现层也被成为Controller层,因为在前后端分离开发之后流行,后端一般不需要返回HTML页面了,而是直接返回JSON数据给前端即可,数据的解析和页面展示展示由前端框架负责。而在基于三层架构的Web应用中,Model也可以指全部的单纯的数据模型,也就是JavaBean对象,而业务功能由业务层承担了。也就是说MVC中的,View和Model要么很少使用了,要么有了新的意义。
  1. 业务层,业务层一般不需要框架,因为它主要是一些业务逻辑代码。
  1. 也就是我们常说的service层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但是业务层不依赖web层。
  2. 业务层在业务处理时可能会依赖持久层,比如操作数据库数据。
  1. 持久层,框架有比如mybatis和hibernate。
  1. 也就是我们是常说的dao层或者mapper层。负责对于数据的访问和操作,以及数据持久化,数据库是对数据进行持久化的载体,持久层是业务层和数据库进行交互的接口,业务层需要通过持久层来访问和操作数据库数。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。

总的来说,MVC模式与三层架构结合使用时的关系(采用SSM框架,未实现前后端分离):

spring mvc解析html spring web mvc_MVC

如果是基于前后端分离的项目,所有请求都是返回JSON字符串给前端,那么由于项目不再需要JSP、freemarker等技术以及前端资源,不再需要响应HTML页面,因此对应的View视图层或许也可以不需要了,或者说View的工作由前端框架来实现了。

一个具体的例子,项目中的资源以及对应的位置:

  1. users.jsp (View)
  2. UserController.java (Controller)
  3. UserVO.java、UserDTO.java、UserDO.java (Model)
  4. UserService.java、UserServiceImpl.java (Service层)
  5. UserDao.java、UserMapper.java (DAO层)

2 Spring MVC的入门案例

我们基于maven和XML配置来构建最简单的Spring MVC入门案例!

2.1 项目搭建

首先创建一个传统的maven Java Web项目:

spring mvc解析html spring web mvc_java_02

创建成功之后结构如下:

spring mvc解析html spring web mvc_java_03

接下来我们开始改造为Spring MVC项目!

在main目录下新增目录:

spring mvc解析html spring web mvc_java_04

我们要添加的就是java目录和resources目录,在高版本Idea中有智能提示:

spring mvc解析html spring web mvc_MVC三层架构_05

没有提示也没关系,手动创建这两个名字的目录,然后分别选中两个目录,右键单击,将java目录设置为Sources Root,表示这是一个源码目录,用于存放Java源码,将resources目录设置为Resources Root,表示这是一个资源目录,用于存放配置文件。

spring mvc解析html spring web mvc_spring mvc解析html_06

对于一个完整的Web项目,应该还要有测试目录!我们选中src目录,创建目录:

spring mvc解析html spring web mvc_MVC三层架构_07

在src下面就可以创建测试代码目录和测试资源目录:

spring mvc解析html spring web mvc_java_08

完整的项目结构如下:

spring mvc解析html spring web mvc_MVC三层架构_09

2.2 maven依赖

接下来我们添加依赖。

我们只需要添加一个spring-webmvc依赖,即可实现最简单的Spring MVC项目的测试,spring-webmvc依赖包含了spring-web依赖以及Spring框架的核心依赖,比如spring-core、spring-context等等。

同时,我们采用maven tomcat7插件的形式直接启动项目,方便快捷!

<properties>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-framework.version>5.2.8.RELEASE</spring-framework.version>
</properties>

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring-framework.version}</version>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
        </plugins>
    </pluginManagement>
    <!--打包名-->
    <finalName>mvc</finalName>
    <plugins>
        <!-- tomcat插件控制 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
                <!--端口控制-->
                <port>8081</port>
                <!--项目路径控制意味着http://localhost:8081/mvc-->
                <path>/mvc</path>
                <!--tomcat的url编码  达到和修改server.xml文件一样的功能-->
                <uriEncoding>UTF-8</uriEncoding>
            </configuration>
        </plugin>

        <!-- maven插件控制 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
                <encoding>${project.build.sourceEncoding}</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

2.3 添加Spring MVC配置文件

我们在resources目录下添加一个专门Spring MVC的配置文件! 可以叫dispatcher-servlet.xml或者spring-mvc-config.xml等等(名字自取),如果一个项目的组件非常多,那么通常一个组件都有一个单独的配置文件,Spring MVC也不例外!

如下文件,我们最简单的案例仅仅需要配置一个扫描注解的包路径,并且这是属于Spring支持的最基本配置,而不是Spring MVC特有的。

<?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">

    <!-- 配置要扫描注解的包路径 -->
    <context:component-scan base-package="com.spring.mvc.controller"/>
</beans>

2.4 编写view视图文件

我们在webapps下新建一个success.jsp文件,简单的内容如下:

<html>
<body>
<h2>Hello Spring MVC!</h2>
</body>
</html>

最终配置如下:

spring mvc解析html spring web mvc_MVC三层架构_10

2.5 配置DispatcherServlet

DispatcherServlet是一个特殊的Servlet,是Spring MVC中非常核心的一个前端控制器,用于处理、分发所有的请求!在web.xml中配置这一个Servlet就行了。

我们需要将spring-mvc-config.xml配置到contextConfigLocation参数中用于Servlet WebApplicationContext容器加载配置,后面我们会讲Spring MVC的容器关系!

设置load-on-startup为非负数,实际上不设置或者设置为负数也没关系,但是DispatcherServlet将会在请求到达的时候才会加载各种配置信息,因此如果配置有什么异常的话将不能在项目启动时被及时发现!

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!-- 配置spring mvc的前端核心控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <!-- 配置初始化参数,用于读取SpringMVC的配置文件并加载 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc-config.xml</param-value>
        </init-param>
        <!-- 配置Servlet的对象的创建时间点:取值如果为非负整数,表示应用加载时创建,值越小,servlet的优先级越高,就越先被加载,如果取值为负数,表示在第一次使用时才加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--配置映射路径,/表示将会处理所有的请求,并且自动分发-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2.6 编写Controller控制器

编写第一个控制器类com.spring.mvc.controller.HelloController。sayHello方法表示来自/hello的请求将会访问success.jsp资源!

/**
 1. 第一个控制器类
 2.  3. @author lx
 */
@Controller
public class HelloController {

    /**
     * 请求路径映射到某个方法
     * /hello路径映射到sayHello()方法
     */
    @RequestMapping(path = "/hello")
    public String sayHello() {
        System.out.println("Hello SpringMVC");
        //转发到success.jsp的视图
        return "success.jsp";
    }
}

2.7 启动测试

我们启动maven tomcat 插件进行测试,有两种方法。

一种是在右侧maven栏中,找到Plugins下的tomcat7插件,双击tomcat7:run即可启动项目!

spring mvc解析html spring web mvc_MVC_11

第二种是配置maven启动项!

我们选择Edit Configurations并点击

spring mvc解析html spring web mvc_MVC三层架构_12

选择+,选择maven:

spring mvc解析html spring web mvc_springmvc_13

找到我们的项目,并配置tomcat7:run参数,点击Apply之后即可正常点击右侧的按钮运行!

spring mvc解析html spring web mvc_java_14

启动之后将自动输出日志信息,可以看到我们项目的主路径,以及dispatcherServlet被初始化的信息!

spring mvc解析html spring web mvc_MVC_15

我们尝试访问http://localhost:8081/mvc/hello,成功获取到HTML视图信息,第一例到此结束!

spring mvc解析html spring web mvc_MVC三层架构_16

2.8 简单的执行流程

我们简单的介绍Spring MVC的执行流程,注意这里的流程非常简单,我们后面会讲解详细的流程!

spring mvc解析html spring web mvc_java_17

  1. tomcat服务器启动,应用被加载,读取到web.xml中的配置,在创建servlet的同时加载了spring-config.xml配置文件,即创建spring容器并且初始化容器中的对象,从入门案例中可以看到的是HelloController,但是我们知道Spring容器启动的时候远远不止加载这些类。实际上它会创建两个容器,一个是SpringMVC的容器,另一个是Spring父容器,这个我们后面学习源码时就知道了。
  2. 浏览器发送请求,被DispatherServlet捕获(其路径是“/”表示所有请求都要经过该Servlet)。但是该Servlet并不处理请求,而是把请求转发出去,此时控制台打印。转发的路径是根据请求URL,匹配@RequestMapping中的内容。
  3. 匹配到了后,找到方法并执行对应方法,该方法有一个返回值。
  4. 根据方法的返回值,借助InternalResourceViewResolver(视图解析器,虽然我们没有配置,但是Spring mVC会自动配置)找到对应的结果视图,也就是success.jsp。
  5. 渲染结果视图为html文本,响应给浏览器,浏览器渲染页面。

2.9 基于JavaConfig的配置

基于JavaConfig的配置则可以通过Spring 提供的WebApplicationInitializer抽象类来配置。使用该配置我们必须引入Servlet的依赖,同时配置中的AppConfig类作为一个主配置类,其内部通过JavaConfig的方式(比如@Bean方法)配置各种bean!

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>
public class MyWebApplicationInitializer implements 
WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        /*
         * 创建一个Spring web应用容器,加载AppConfig配置类
         */
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        /*
         * 创建并注册DispatcherServlet,关联上的容器
         */
        DispatcherServlet servlet = new DispatcherServlet(ac);
        //命名并注册
        ServletRegistration.Dynamic registration = servletCxt.addServlet("dispatcherServlet", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}
@ComponentScan("com.spring.mvc")
@Configuration
public class AppConfig { }

如果采用基于JavaConfig的配置,那么web.xml中可以不提供任何配置,其他的xml配置文件也都应该改为JavaCOnfig的形式!

最终项目图如下:

spring mvc解析html spring web mvc_spring mvc解析html_18

启动项目即可测试成功!如果你仍然需要基于XML文件,那么你可以换一个WebApplicationContext的实现,比如:

public class MyXmlWebApplicationInitializer implements 
WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        /*
         * 创建Spring web应用容器,加载AppConfig配置类
         */
        XmlWebApplicationContext ac = new XmlWebApplicationContext();
        ac.setConfigLocation("classpath:spring-mvc-config.xml");
        /*
         * 创建并注册DispatcherServlet
         */
        DispatcherServlet servlet = new DispatcherServlet(ac);
        //命名并注册
        ServletRegistration.Dynamic registration = servletCxt.addServlet("dispatcherServlet", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

另外,还可以使用继承AbstractAnnotationConfigDispatcherServletInitializer的方式进行配置更多的配置!