跳转至

MVC架构模式#

约 2664 个字 156 行代码 预计阅读时间 58 分钟

不使用MVC模式存在的问题#

  • 不使用MVC模式,Servlet需要负责数据接收核心业务逻辑处理数据库连接和增删改查操作页面展示等功能。职责过重。
  • 代码的复用性差,相同的业务操作或数据库操作,需要在不同Servlet中编写重复代码,不方便维护。
  • 代码耦合度高,导致代码很难扩展。
  • 操作数据库的代码和处理业务逻辑的代码混杂在一起,很容易出错,无法专注于业务逻辑的编写。

MVC模式概述#

  • M(Model、模型):用于处理业务
  • V(View、视图):负责页面展示
  • C(Controller、控制器):控制器是MVC架构的核心,
MVC架构图解
MVC请求响应过程

DAO(Data Access Object、数据访问对象)

属于JavaEE的设计模式之一。只负责数据库的增删改查,没有任何业务逻辑在里面

三层架构#

  • 表示层:Controller控制器+View视图
  • 业务逻辑层:Service服务
  • 持久化层:DAO数据访问对象

Spring MVC概述#

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从 Spring 框架诞生之初就已包含其中。其正式名称Spring Web MVC 源自其源码模块名称:spring-webmvc,但更常被称为Spring MVC

与 Spring Web MVC 并行,Spring Framework 5.0 引入了一个名为Spring WebFlux的响应式堆栈Web框架,其名称也基于其源模块 spring-webflux

Spring MVC是实现MVC架构模式的Web框架。底层使用Servlet实现。

Spring MVC能干什么

  • 入口控制:通过DispatcherServlet作为入口控制器负责接收请求和分发请求。
  • 自动将表单请求参数封装为JavaBean对象
  • 统一使用IOC容器管理对象
  • 统一请求处理:提供拦截器、统一异常处理等机制
  • 视图解析:轻松切换JSP、Freemarker、Velocity等视图模板
  • 对Controller进行单元测试时
  • 入口控制:Servlet开发中,每个Servlet都需要在web.xml中进行配置,Spring MVC通过DispatcherServlet作为入口控制器负责统一接收请求和分发请求。
  • Spring MVC会自动将表单数据封装为JavaBean对象,而不需要手动通过request对象获取表单数据。
  • Spring MVC通过IOC容器管理对象,不需要手动创建对象。
  • Spring MVC提供拦截器、异常处理器等统一处理请求机制。不需要手动编写过滤器。
  • 视图解析器:Spring MVC提供了JSP、Freemarker、Velocity等视图解析器。

Spring MVC入门案例#

创建maven工程,将工程改为war包,引入依赖:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.luguosong</groupId>
    <artifactId>springmvc-hello</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--Spring MVC依赖中包含了Spring的相关依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.1.8</version>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <!--provided表示该依赖只在编译和测试时有效-->
            <!--打war包时不会包含,由Tomcat提供-->
            <scope>provided</scope>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!--thymeleaf与spring6整合-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring6</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Warning

注意,需要将maven工程改为war包:<packaging>war</packaging>

创建webapp/WEB-INF/web.xml目录和文件。

web.xml中配置前端控制器(DispatcherServlet):

Note

相比于Servlet开发,Spring MVC会配置一个全局统一的DispatcherServlet来管理所有请求。

XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--优化:让DispatcherServlet在启动时就加载,而不是首次访问时加载,提高首次访问速度-->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--url-pattern配置/表示处理除jsp外的所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

在Spring MVC配置文件配置包扫描视图解析器:

其中常见的视图解析器有以下几种:

  • JSP的视图解析器:InternalResourceViewResolver
  • FreeMarker的视图解析器:FreeMarkerViewResolver
  • Velocity的视图解析器:VelocityViewResolver
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 http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置组件扫描-->
    <context:component-scan base-package="com.luguosong.controller"/>

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

编写视图:

hello.thymeleaf

编写Controller:

Java
package com.luguosong.controller;

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

/**
 * @author luguosong
 */
@Controller
public class HelloController {
    //请求映射
    @RequestMapping("/hello-mvc")
    public String hello() {
        //返回逻辑视图名称
        return "hello";
    }
}

Note

逻辑视图名称会根spring mvc配置文件中的prefixsuffix属性进行拼接。找到具体的视图位置(物理视图名称)。

启动Tomcat,通过以下地址可以访问视图:

Text Only
http://localhost:8080/springmvc_hello_war_exploded/hello-mvc

Spring MVC执行流程#

Spring MVC执行流程

自定义Spring MVC配置文件名称#

默认情况下,Spring MVC会根据web.xml中<servlet-name>标签的值去寻找Spring MVC配置文件。

比如<servlet-name>的值为springmvc,那么就会去寻找/WEB-INF/springmvc-servlet.xml配置文件。

当然,也可以手动指定Spring MVC配置文件:

XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--指定配置文件位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

@RequestMapping注解#

@RequestMapping注解的使用#

您可以使用@RequestMapping注解将请求映射到控制器方法。

@RequestMapping可以作用于或者方法

value属性#

value属性path属性功能相同,都是用于映射请求路径。

Java
@Controller
public class HelloController {
    //请求映射
    @RequestMapping("/hello1-1")
    public String hello() {
        //返回逻辑视图名称
        return "hello";
    }

    //多个映射可以指向同一个方法
    @RequestMapping({"/hello2-1", "/hello2-2"})
    public String hello() {
        //返回逻辑视图名称
        return "hello";
    }
}

value属性Ant风格#

value属性也支持Ant风格的通配符:

  • ?:匹配任意单个字符
  • *:匹配任意多个字符
  • **:匹配任意多个字符(包括目录,即/

Warning

如果使用**,左右两边只能是/

Java
@Controller
public class HelloController {
    //?表示任意单个字符,比如 hello1 或 helloa 都会访问到该方法
    @RequestMapping("/hello?")
    public String hello() {
        //返回逻辑视图名称
        return "hello";
    }
}

value属性占位符#

使用占位符,可以实现Restful风格的参数传递

方法中通过@PathVariable获取参数

Java
@Controller
public class HelloController {
    @RequestMapping("/login/{username}/{password}")
    public String login(@PathVariable String username,
                        @PathVariable String password) {
        //用户登录
        //...
        return "ok";
    }
}

method属性#

method属性用于限制请求方法,method属性的值可以是GETPOSTPUTDELETE等。

Java
@Controller
public class HelloController {
    //只会接收Get类型的请求
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        //返回逻辑视图名称
        return "hello";
    }
}

衍生Mapping#

除了@RequestMapping注解,还可以使用@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping 等注解。表示具体method方法的请求。

params属性#

params属性对请求参数进行限制

Java
@Controller
public class HelloController {
    //表示请求参数中必须存在username和password,且username必须为张三
    @PostMapping(value = "/hello", params = {"username=张三", "password"})
    public String hello() {
        //返回逻辑视图名称
        return "ok";
    }
}

headers属性#

headers属性对请求头进行限制

Java
@Controller
public class HelloController {
    //表示请求头中必须存在token
    @PostMapping(value = "/hello", headers = {"token"})
    public String hello() {
        //返回逻辑视图名称
        return "ok";
    }
}

获取请求参数#

形参获取表单请求参数#

FormController.java
package com.luguosong.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/form")
public class FormController {

    @GetMapping("/formView")
    public String formView() {
        return "get-parameters/form";
    }

    /*
     * 模拟Servlet接收参数
     * */
    @PostMapping("/servlet")
    public String servletPost(HttpServletRequest request) {
        request.setAttribute("username", request.getParameter("username"));
        return "get-parameters/form";
    }

    /*
     * @RequestParam注解value值为请求参数名
     * required属性表示参数是否必须,默认为true
     * defaultValue属性为默认值
     * */
    @PostMapping("/springMvc")
    public String springMvc(
            @RequestParam(
                    value = "username",
                    required = false,
                    defaultValue = "张三") String username,
            HttpServletRequest request) {
        request.setAttribute("username", username);
        return "get-parameters/form";
    }


    /*
     * 如果请求参数的形参名和方法参数名一样,则@RequestParam注解可以省略
     * */
    @PostMapping("/springMvc2")
    public String springMvc2(
            String username,
            HttpServletRequest request) {
        request.setAttribute("username", username);
        return "get-parameters/form";
    }
}

如果是Spring6+,想要省略@RequestParam注解,需要在pom.xml中配置-parameters标记

XML
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.13.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

JavaBean获取表单请求参数#

FormPojoController.java
package com.luguosong.controller;

import com.luguosong.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/formPojo")
public class FormPojoController {

    @PostMapping("/springMvc")
    public String springMvcPojo(
            User user,
            HttpServletRequest request) {
        request.setAttribute("username", user.getUsername());
        return "get-parameters/form";
    }
}
User.java
package com.luguosong.pojo;

/**
 * @author luguosong
 */
public class User {
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

Get请求中文乱码问题#

Tomcat8以及之前版本,解决Get请求中文乱码,在Tomcat服务器CATALINA_HOME/conf/server.xml中配置:

解决Get请求乱码问题

Tomcat8之后版本,请求行默认采用UTF-8编码,无需解决中文乱码问题。

Post请求中文乱码问题#

Tomcat9以及之前的版本,需要解决Post请求中文乱码问题。

Servlet编程中,可以使用request.setCharacterEncoding("UTF-8");解决乱码问题。

但在Spring MVC中,无法在Controller中使用以上方法解决中文乱码。

解决方案一:编写Servlet过滤器,过滤器会在DispatcherServlet之前执行。因此在过滤器中设置 request.setCharacterEncoding("UTF-8"); 可以解决乱码问题。

解决方案二:Spring MVC为我们提供了类似的过滤器类CharacterEncodingFilter,无需我们重新手写过滤器类。只需要在web.xml 中配置该过滤器并设置encoding属性即可。

Tomcat10请求体默认采用UTF-8编码,无需解决中文乱码问题。

获取请求头信息#

根据请求头名称获取请求头信息#

HeaderInfoController.java
package com.luguosong.controller.header_info;

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

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/header-info")
public class HeaderInfoController {

    @PostMapping("/springMvc")
    public String springMvc(@RequestHeader(value = "content-type", required = false) String contentType) {
        System.out.println(contentType);
        return "header-info/form";
    }
}
CookieController.java
package com.luguosong.controller.header_info;

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

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/cookie")
public class CookieController {

    /*
     * ❗cookie需要在父路径相同的情况才能保存下来
     * 因此通过该地址访问视图
     * */
    @GetMapping("/view")
    public String view() {
        return "header-info/form";
    }

    @RequestMapping("/springMvc")
    public String springMvc(@CookieValue("username") String username) {
        System.out.println(username);
        return "header-info/form";
    }
}

域对象操作#

request域#

RequestScopeController.java
package com.luguosong.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/request-scope")
public class RequestScopeController {

    /**
     * 模拟Servlet存储Request域
     */
    @RequestMapping("/servlet")
    public String servletTest(HttpServletRequest request) {
        request.setAttribute("requestScope", "通过HttpServletRequest设置请求域");
        return "request-scope";
    }

    /*
     * Model对象存储request域
     * */
    @RequestMapping("/model")
    public String modelTest(Model model) {
        model.addAttribute("requestScope", "通过Model对象设置请求域");
        return "request-scope";
    }

    /*
     * Map集合存储request域
     * */
    @RequestMapping("/map")
    public String mapTest(Map<String, Object> map) {
        map.put("requestScope", "通过Map对象设置请求域");
        return "request-scope";
    }

    /*
     * ModelMap对象存储request域
     * */
    @RequestMapping("/modelMap")
    public String modelMapTest(ModelMap modelMap) {
        modelMap.addAttribute("requestScope", "通过ModelMap对象设置请求域");
        return "request-scope";
    }

    /*
     * ⭐ModelAndView对象存储request域
     * */
    @RequestMapping("/modelAndView")
    public ModelAndView modelAndViewTest() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("requestScope", "通过ModelAndView对象设置请求域");
        modelAndView.setViewName("request-scope");
        return modelAndView;
    }


}
BindingAwareModelMap类结构

不管是Model对象Map集合还是ModelMap对象,实际创建的的都是BindingAwareModelMap对象

Spring MVC为了更好的体现MVC架构模式,还提供了ModelAndView类 ,这个类封装了Model和View。也就是说这个类封装业务处理之后的数据,体支持跳转指定视图。通过ModelAndView也可以设置请求域。

session域#

SessionScopeController.java
package com.luguosong.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/session-scope")
@SessionAttributes({"sessionScope"})
public class SessionScopeController {

    /*
    * 通过Servlet方式设置session
    * */
    @RequestMapping("/servlet")
    public String servletTest(HttpServletRequest request) {
        request.getSession().setAttribute("sessionScope", "通过Servlet原生方式设置session域");
        System.out.println(request.getSession().getAttribute("sessionScope"));
        return "session-scope";
    }

    /*
    * 通过@SessionAttributes注解设置session
    * */
    @RequestMapping("/modelAndView")
    public ModelAndView modelAndViewTest() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("sessionScope", "通过@SessionAttributes注解方式设置session域");
        modelAndView.setViewName("session-scope");
        return modelAndView;
    }
}

一般情况下modelAndView.addObject是设置request域的,但通过@SessionAttributes({"xxx"})注解可以指定特定字段为session域。

application域#

ApplicationScopeController.java
package com.luguosong.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author luguosong
 */
@Controller
@RequestMapping("/application-scope")
public class ApplicationScopeController {

    @RequestMapping("/servlet")
    public String servletTst(HttpServletRequest request) {
        request.getServletContext().setAttribute("applicationScope", "通过Servlet方式设置application域");
        return "application-scope";
    }
}

一般直接采用Servlet原始方式设置application域。

视图(View)#

常见的视图#

  • InternalResourceView:内部资源视图,Spring MVC框架内置,专门为JSP模板语法准备
  • RedirectView:重定向视图,Spring MVC框架内置,用来完成重定向效果
  • ThymeLeafView:Thymeleaf 是一种现代化的服务器端 Java 模板引擎,适用于网页和独立环境。Thymeleaf 的主要目标是为您的开发流程带来优雅的自然模板——这些 HTML 可以在浏览器中正确显示,同时也能作为静态原型使用,从而增强开发团队的协作。它提供了 Spring Framework 的模块、与您喜爱的工具的多种集成,并允许您插入自己的功能,因此 Thymeleaf 非常适合现代 HTML5 JVM 的网页开发——尽管它的功能远不止于此。
  • FreeMarkerView:Apache FreeMarker™ 是一个模板引擎:它是一个 Java 库,用于根据模板和变化的数据生成文本输出(如 HTML 网页、电子邮件、配置文件、源代码等)。模板使用 FreeMarker 模板语言(FTL)编写,这是一种简单的专用语言(不像 PHP 那样是完整的编程语言)。通常,会使用通用编程语言(如 Java)来准备数据(执行数据库查询、进行业务计算)。然后,Apache FreeMarker 使用模板展示这些准备好的数据。在模板中,你专注于如何展示数据,而在模板之外,你专注于展示哪些数据。
  • VelocityView:VelocityView 包含所有的 GenericTools,并增加了在 Web 应用程序(Java EE 项目)视图层中使用 Velocity 的基础设施和专用工具。这包括用于处理 Velocity 模板请求的 VelocityViewServlet 或 VelocityLayoutServlet,以及用于在 JSP 中嵌入 Velocity 的 VelocityViewTag。
  • PDFView:第三方,用于生成pdf文件视图
  • ExcelView:第三方,用于生成excel文件视图

配置JSP视图解析器#

springmvc-servlet.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 http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置组件扫描-->
    <context:component-scan base-package="com.luguosong.controller"/>

    <!--配置JSP视图解析器-->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/templates/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

配置Thymeleaf视图解析器#

springmvc-servlet.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 http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置组件扫描-->
    <context:component-scan base-package="com.luguosong.controller"/>

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

视图控制器#

如何仅仅是进行视图转发,无需编写Controller类,可以通过spring mvc配置文件mvc:view-controller标签进行配置。

springmvc-servlet.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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--配置组件扫描-->
    <context:component-scan base-package="com.luguosong.controller"/>

    <!--配置JSP视图解析器-->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/templates/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--在Spring MVC配置文件中配置视图控制器-->
    <mvc:view-controller path="/test" view-name="test"/>

    <!--<mvc:view-controller/>会让Spring MVC项目中的注解失效,需要重新开启-->
    <!--开启注解驱动-->
    <mvc:annotation-driven/>
</beans>

转发和重定向#

Java
package com.luguosong.controller;

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

/**
 * @author luguosong
 */
@Controller
public class TestController {

    /*
    * 转发
    * */
    @RequestMapping("forward")
    public String forwardTest() {
        return "forward:/test";
    }

    /*
    * 重定向
    * */
    @RequestMapping("redirect")
    public String redirectTest() {
        return "redirect:/test";
    }

    @RequestMapping("/test")
    public String test() {
        return "test";
    }
}

静态资源访问#

由于DispatcherServlet的url-pattern配置的是/,访问静态资源会经过DispatcherServletDispatcherServlet没有静态资源处理。

方式一:开启默认Servlet#

Tomcat目录conf/web.xml中存在名为default的servlet

DefaultServlet

url-patternDispatcherServlet一样也是/,因此默认servlet访问被DispatcherServlet覆盖。

default Servlet servlet-mapping

Spring MVC仍然允许静态资源请求由Tomcat的默认Servlet处理。它配置了一个DefaultServletHttpRequestHandler,URL映射为/** ,并且相对于其他URL映射具有最低优先级

以下示例展示了如何通过默认设置启用该功能:

Java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

以下示例展示了如何在Spring MVC配置文件 XML 中实现相同的配置:

XML
<beans>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>
</beans>

方式二:配置静态资源处理#

在下一个示例中,对于以 /resources 开头的请求,将使用相对路径来查找和提供相对于 Web 应用程序根目录下的 /public 或类路径下的 /static 的静态资源。这些资源的过期时间设置为一年,以确保最大限度地利用浏览器缓存并减少浏览器发出的 HTTP 请求。Last-Modified 信息通过 Resource#lastModified 推断,以支持带有 "Last-Modified" 头的 HTTP 条件请求。

以下列表展示了如何使用 Java 配置来实现:

Java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public", "classpath:/static/")
                .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
    }
}

以下示例展示了如何在 XML 中实现相同的配置:XML

XML
<beans>
    <mvc:resources mapping="/resources/**"
                   location="/public, classpath:/static/"
                   cache-period="31556926"/>
    <mvc:annotation-driven/>
</beans>

RESTFul#

概述#

RESTFul是web服务接口的一种设计风格。提供了一套约束,可以让web服务接口更加简介、易于理解。

  • 查询:使用GET方法请求
  • 添加:使用POST方法请求
  • 更新:使用PUT方法请求
  • 删除:使用DELETE方法请求

请求参数从/springmvc/getUserById?id=1风格转为/springmvc/user/1风格,变得更加简洁。

示例#

模拟通过表单发送getpostputdelete请求。

Warning

理论上表单只能发送get请求post请求

但是可以借助HiddenHttpMethodFilter过滤器,将post方法转为putdeletepatch方法。

评论