Spring MVC详解(上)

一、Spring MVC初步认识

1.1介绍

Spring MVC是Spring Framework提供的Web组件,全称是Spring Web MVC,是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能

Java Web开发者必须要掌握的技术框架

1.2MVC是什么

MVC是一种软件架构思想,把软件按照模型,视图,控制器来划分

View:视图层,指工程中的html,jsp等页面,作用是和用户进行交互,展示数据

Controler:控制层,指工程中的Servlet,作用是接收请求和响应浏览器

Model:模型层,指工程中的JavaBean,用来处理数据

JavaBean分成两类:

  • 一类称为实体类Bean:专门用来存储业务数据,比如Student,User
  • 一类称为业务处理Bean:指Servlet或Dao对象,专门用来处理业务逻辑和数据访问

流程:

  1. 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收
  2. Controller调用相应的Model层处理请求,处理完毕后结果返回到Controller
  3. Controller再根据请求处理的结果找到对应的View视图,渲染数据后最终响应给浏览器

Spring MVC对这套MVC流程进行封装,帮助开发者屏蔽底层细节,并且开放出相关接口供开发者调用,让MVC开发更简单方便

1.3 核心组件

  • DispatcherServlet:前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Spring MVC的核心模块
  • Handler:处理器,完成具体的业务逻辑,相当于Servlet
  • HandlerMapping:DispatcherServlet是通过 HandlerMapping把请求映射到不同的Handler
  • HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成
  • HandlerExecutionChain:处理器执行链,包括两部分内容:Handler和HandlerInterceptor(系统会有一个默认的HandlerInterceptor,如果有额外拦截处理,可以添加拦截器进行设置)
  • HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型转换、把表单数据封装到POJO等,这些一系列的操作都是由
  • HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler
  • ModelAndView:封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet
  • ViewResolver:视图解析器,DispatcherServlet通过它把逻辑视图解析为物理视图,最终把渲染的结果响应给客户端

1.4 工作流程

  1. 客户端请求被DispatcherServlet接收
  2. 根据HandlerMapping映射到Handler
  3. 生成Handler和HandlerInterceptor
  4. Handler和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet
    DispatcherServlet通过HandlerAdapter调用Handler的方法完成业务逻辑处理
  5. 返回一个ModelAndView对象给DispatcherServlet
  6. DispatcherServlet把获取的ModelAndView对象传给ViewResolver视图解析器,把逻辑视图解析成物理视图
  7. ViewResolver返回一个View进行视图渲染(把模型填充到视图中)
  8. DispatcherServlet把渲染后的视图响应给客户端

二、Spring MVC环境搭建

2.1创建maven工程,修改pom.xml加入Spring MVC的依赖







pom.xml

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>org.example</groupId>
  <artifactId>SpringMVC</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>SpringMVC Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

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

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.19</version>
    </dependency>

  </dependencies>


</project>

2.2在web.xml中配置Spring MVC的DispatcherServlet

  1. 首先在项目中创建java和resources的目录
  2. 在resources目录中添加springmvc.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
  1. 然后在web.xml 配置Spring MVC的DispatcherServlet
xml 复制代码
<!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>
 <!-- 配置核心控制器 -->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- springmvc配置文件加载路径
		     1)默认情况下,读取WEB-INF下面的文件
		     2)可以改为加载类路径下(resources目录),加上classpath:
		 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		<!--
		   DispatcherServlet对象创建时间问题
		      1)默认情况下,第一次访问该Servlet的创建对象,意味着在这个时间才去加载springMVC.xml
		      2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
		          <load-on-startup>1</load-on-startup>
		                数值越大,对象创建优先级越低! (数值越低,越先创建)
		-->
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		 <!--/ 匹配所有的请求;(不包括.jsp)-->
   <!--/* 匹配所有的请求;(包括.jsp)-->
    <!--*.do拦截以do结尾的请求-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
  
  
</web-app>

拦截请求其实就是说,只要是符合这个URL的请求都会进入到Spring MVC中去看看有没有对应的Handler./不会拦截.jsp的路径,但是会拦截.html等静态资源

  • DispatcherServlet是Spring MVC提供的核心控制器,这个一个Servlet程序,该Servlet程序会接收所有请求
  • 核心控制器会读取一个springmvc.xml配置,加载Spring MVC的核心配置
  • 配置/代表拦截所有请求
  • 代表在项目启动时实例化DispathcerServlet,如果没有配置,则在第一次访问Servlet时进行实例化
  1. springmvc.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"
       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.zyh.controller"></context:component-scan>

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--给逻辑视图加上前缀和后缀 -->
        <!--前缀-->
        <property name="prefix" value="/"></property>
        <!--后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>
  1. 创建Controller控制器Handler,在里面编写接收参数,调用业务方法,返回视图页面等逻辑
java 复制代码
@Controller
public class HelloHandler {
    /**
     * 当客户端访问index请求时
     * 直接自动关联到这个方法
     * 执行这个方法后,会返回结果
     * @return
     */
    @RequestMapping("/index")
    public String index(){
        System.out.println("接收到了请求");
        //返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图
        //这里返回的只是页面的名称,不是完整的页面访问路径
        return "index";
    }
}

@Controller注解是为了让Spring IOC容器初始化时自动扫描到该Controller类;@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/;方法返回的结果是视图的名称index,该名称不是完整页面路径,最终会经过视图解析器解析为完整页面路径并跳转。

  1. 配置Tomcat

  2. 测试


三、@RequestMapping注解

Spring MVC通过@RequestMapping注解把URL请求和业务方法进行映射,在控制器的类定义处以及方法定义处都可以添加@RequestMapping,在类定义处添加相当于多了一层访问路径

@RequestMapping常用参数:

  • value:指定URL请求的实际地址,是@RequestMapping的默认值
  • method:指定请求的method类型,包括GET、POST、PUT、DELETE等
  • params:指定request请求中必须包含的参数值,如果不包含的话,就无法调用该方法
java 复制代码
   @RequestMapping(value = "/index",method = RequestMethod.POST,params="id")
    public String index(){
        System.out.println("接收到了请求");
        //返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图
        //注意:这里返回的只是页面名称,不是完整的页面访问路径
        return "index";
    }

四、参数绑定

4.1 URL风格参数绑定

params是对URL请求参数进行限制,不满足条件的URL无法访问该方法,需要在业务方法中获取URL的参数值。

  1. 在业务方法定义时声明参数列表
  2. 给参数列表添加@RequestParam注解进行绑定




    Spring MVC可以自动完成数据类型转换,该工作是由HandlerAdapter来完成的

4.2 RESTful风格的URL参数获取

  • 传统的URL:localhost:8080/hello/index?id=1&name=tom
  • RESTful URL:localhost:8080/hello/index/1/tom
java 复制代码
 @RequestMapping("/restful/{id}/{name}")
    public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
        System.out.println(num+"-"+name);
        return "index";
    }

4.3映射Cookie

java 复制代码
  @RequestMapping("/cookie")
    public String getCookie(@CookieValue("JSESSIONID") String sessionId){
        System.out.println(sessionId);
        return "index";
    }

4.4使用POJO绑定参数

Spring MVC会根据请求参数名和POJO属性名进行匹配,自动为该对象填充属性值,并且支持属性级联

首先创建实体类

为了方便测试,写一个addUser.jsp页面

html 复制代码
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/hello/add" method="post">
    <table>
        <tr>
            <td>编号:</td>
            <td>
                <input type="text" name="id">
            </td>
        </tr>
        <tr>
            <td>姓名:</td>
            <td>
                <input type="text" name="name">
            </td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>


</form>

</body>
</html>

然后在Handler中,编写相关方法

启动Tomcat服务器

结果发现出现乱码问题

为了解决这个问题,我们只需要在web.xml配置文件中配置过滤器就可以了

xml 复制代码
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

4.5JSP页面的转发和重定向

Spring MVC默认是通过转发的形式响应JSP,可以手动进行修改

java 复制代码
    @RequestMapping("/restful/{id}/{name}")
    public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
        System.out.println(num+"-"+name);
        return "index";
    }

重定向:

java 复制代码
    @RequestMapping("/restful/{id}/{name}")
    public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
        System.out.println(num+"-"+name);
        return "redirect:/index.jsp";
    }


设置重定向的时候不能写逻辑视图,必须写明资源的物理路径,比如"rediect:/index.jsp"

转发:

java 复制代码
    @RequestMapping("/restful/{id}/{name}")
    public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){
        System.out.println(num+"-"+name);
        return "forward:/index.jsp";
    }

五、数据绑定

  • 数据绑定:在后台业务方法中,直接获取前端HTTP请求中的参数
  • HTTP请求传输的参数都是String类型的,Handler业务方法中的参数是开发者指定的参数类型,比如int,Object,所以需要进行数据类型的转换
  • Spring MVC的HandlerAdapter组件会在执行Handler业务方法之前,完成参数的绑定,开发者直接使用即可

5.1基本数据类型

java 复制代码
@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
     return "id:"+id;
}

客户端HTTP请求中必须包含id参数,否则抛出500异常,因为id不能为null

同时id的值必须为数值,而且必须为整数,否则抛出400异常

5.2包装类

java 复制代码
 @RequestMapping("/packageType")
 @ResponseBody
 public String packageType(Integer id){
     return "id:"+id;

 }

如果HTPP请求中没有包含id参数,不会报错,id的值就是null,会直接返回id:null给客户端,但是如果id=a,或者id=1.2,同样会抛出404异常,因为数据类型无法转换



  • value="id":把HTTP请求中名字为id的参数和Handler业务方法中的形参进行映射
  • required:true表示id参数必须填,false表示非必填
  • defaultValue="0":表示当HTTP请求中没有id参数的时候,形参的默认值是0

5.3数组类型

java 复制代码
@RequestMapping("/arrayType")
@ResponseBody
public String arrayType(String[] names){
    StringBuffer buffer = new StringBuffer();
    for (String str:names){
        buffer.append(str).append(" ");
    }
    return "names:"+buffer.toString();
}

5.4POJO(java对象)

java 复制代码
public class User {
    private Integer id;
    private String name;
    private Address address;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
java 复制代码
public class Address {
    private Integer code;
    private String value;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Address{" +
                "code=" + code +
                ", value='" + value + '\'' +
                '}';
    }
}
html 复制代码
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/hello/add" method="post">
    <table>
        <tr>
            <td>编号:</td>
            <td>
                <input type="text" name="id">
            </td>
        </tr>
        <tr>
            <td>姓名:</td>
            <td>
                <input type="text" name="name">
            </td>
        </tr>
        <tr>
            <td>地址编号:</td>
            <td>
                <input type="text" name="address.code">
            </td>

        </tr>
        <tr>
            <td>地址信息:</td>
            <td>
                <input type="text" name="address.value">
            </td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>


</form>

</body>
</html>

我们可以在springmvc.xml中添加一个消息转换器把中文乱码解决掉

前后端转换的数据称为消息

解决响应时乱码问题,springmvc.xml中配置转换器即可

5.5 List

Spring MVC不支持List类型的直接转换,需要包装成Object

java 复制代码
public class UserList {
    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }
}

注意:User类一定要有无参构造,否则抛出异常

java 复制代码
@RequestMapping("/listType")
@ResponseBody
public String listType(UserList userList){
    StringBuffer buffer = new StringBuffer();
    for (User user:userList.getUserList()){
        buffer.append(user);
    }
    return "用户:"+buffer.toString();
}

5.6 JSON

  • JSON数据必须用JSON.stringfy()方法转换成字符串
  • contentType:"application/json;charset=UTF-8"不能省略
  • @RequestBody注解
    读取HTTP请求参数,通过SpringMVC提供的HttpMessageConverter接口把读取的参数转换为JSON、XML格式的数据,绑定到业务方法的形参
    需要使用组件结合@RequestBody注解把JSON转为JavaBean,这里使用FastJson,其优势是如果属性为空,就不会将其转为JSON
    把业务方法返回的对象,通过HttpMessageConverter接口转为指定格式的数据,JSON、XML等,响应给客户端
相关推荐
暮乘白帝过重山23 分钟前
Singleton和Prototype的作用域与饿汉式/懒汉式的初始化方式
spring·原型模式·prototype·饿汉式·singleton·懒汉式
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
天上掉下来个程小白3 小时前
Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
java·redis·spring·springboot·苍穹外卖
汤姆大聪明4 小时前
Redisson 操作 Redis Stream 消息队列详解及实战案例
redis·spring·缓存·maven
正经摸鱼5 小时前
classpath与classpath*实现逻辑
后端·spring
良枫5 小时前
Spring Security认证授权深度解析
spring boot·spring