涛声依旧在 发表于 2024-8-16 07:29:14

【SpringMVC】Spring Web MVC入门(一)

https://i-blog.csdnimg.cn/blog_migrate/a7c1f862488583eda18151562265c755.webp?x-image-process=image/format,png#pic_center


前言

前面我们了解了什么是Spring,那么今天我将为各人分享一种在日常网站开辟中使用非常广泛的框架——Spring Web MVC。
什么是Spring Web MVC?

先来看看官方解释。
https://i-blog.csdnimg.cn/blog_migrate/7f2e500d4bc1f0b9af69d087d6d0ac01.png
https://i-blog.csdnimg.cn/blog_migrate/b52c650dacd476ad4e3e20956d733dfd.png
Spring Web MVC是Spring Framework提供的Web组件,它是一个MVC计划模式的框架,主要用于开辟机动、松散耦合的Web应用程序。它提供了模型-视图-控制(Model-View-Controller,简称MVC)的体系结构和可以用来开辟Web应用程序的组件。
Spring Web MVC是一种基于Java的实现了Web MVC计划模式的哀求驱动范例的轻量级Web框架。它使用了MVC架构模式的头脑,将web层进行职责解耦,基于哀求驱动指的就是使用哀求-响应模型,框架的目标就是资助简化日常Web开辟的。Spring Web MVC也是要简化我们日常Web开辟的。
一直提到 MVC 架构模式,那么到底什么是 MVC 呢?
什么是MVC

MVC(Model-View-Controller)架构模型是一种软件计划模式,用于将用户界面、数据模型和控制器分脱离来。这种架构模式可以进步系统的可维护性、可扩展性和机动性。在MVC架构中,模型(Model)负责处理应用程序的数据和业务逻辑,视图(View)负责展示数据给用户,控制器(Controller)负责处理用户哀求并协调模型和视图之间的交互。
https://i-blog.csdnimg.cn/blog_migrate/7791625d5d94bc0fb697536cab605a6d.png


[*]View(视图):指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.
[*]Model(模型):是应⽤程序的主体部门,⽤来处理程序中数据逻辑的部门.
[*]Controller(控制器):可以理解为⼀个分发器,⽤来决定对于视图发来的哀求,必要⽤哪⼀个模型来处理,以及处理完后必要跳回到哪⼀个视图。即⽤来连接视图和模型
MVC架构模式通常应用于桌面应用程序和Web应用程序开辟中。在Web应用程序中,控制器通常由Servlet或Controller类来实现,模型通常由Java类来实现,视图则由HTML或JSP页面来实现。通过MVC架构模式,可以使得程序的各个部门更加独立,更易于维护和扩展。
什么是Spring MVC?

MVC 是一种架构计划模式,也是一种头脑,而 Spring MVC 则是对 MVC 头脑的具体实现,除此之外,Spring MVC 还是一个 Web 架构。
总结来说,Spring MVC 就是一个实现了 MVC 架构模式的 Web 框架。
在前面我们创建 Spring Boot 项目标时候,勾选的 Spring Web 框架其实就是 Spring MVC 框架。
https://i-blog.csdnimg.cn/blog_migrate/b08ee53180462494174092a5fa1a0e33.png
那么这里有 Spring Boot 又有 Spring MVC,那么它们两个之间的区别是什么呢?
Spring Boot 和 Spring MVC 的区别

什么是Spring Boot?

Spring Boot是由Pivotal团队提供的全新框架,其计划目标是用来简化新Spring应用的初始搭建以及开辟过程。该框架使用了特定的方式来进行配置,从而使开辟人员不再必要界说样板化的配置。通过这种方式,Spring Boot致力于在发达发展的快速应用开辟领域(rapid application development)成为领导者。Spring Boot是Spring家属的一个子项目,其计划初衷是为了简化Spring配置,从而可以轻松构建独立运行的程序,并极大进步开辟效率。
关系和区别

Spring MVC是一个用于构建Web应用程序的框架,它基于MVC计划模式,将业务逻辑、数据和界面体现分离,减少了各组件之间的依赖,进步了代码的可重用性。Spring MVC必要手动配置XML或其他范例的配置文件来管理应用程序的各个组件和依赖关系。
Spring Boot则是一个更为当代化的框架,它旨在简化Spring应用的初始搭建以及开辟过程。通过约定优于配置的原则,Spring Boot可以自动配置应用程序的各种组件和依赖关系,制止了繁琐的手动配置过程。此外,Spring Boot还提供了许多内置的功能和工具,如内嵌的Web服务器、自动化测试、安全控制等,使得开辟人员可以更加专注于业务逻辑的实现。
因此,可以说Spring Boot是Spring MVC的一种升级和优化,它通过自动配置和内置的工具简化了开辟过程,进步了开辟效率。同时,Spring Boot也支持使用Spring MVC等其他Spring组件,可以与它们无缝集成。
简单点来说,就是 Spring MVC 是通过 Spring Boot 添加 Spring MVC 依赖来实现 web 功能的。
固然 Spring MVC 实现了 MVC 架构模式,但 Spring MVC 也结合了自身的特点,做了一些改变。
https://i-blog.csdnimg.cn/blog_migrate/17201169842a718ae5f62a6a67a7d36b.png
Spring MVC 学习

在知道了什么是 Spring MVC 之后,我们就可以来学习 Spring MVC 了,而学习 Spring MVC 关键是学习它的注解,由于在 Spring MVC 中,会使用到非常多的注解。
注解介绍

1. @SpringBootApplication

当我们创建完成一个 Spring MVC 项目之后,就会自动生成一个 “项目名”+Application 的类,而且在这个类中我们就可以看到第一个注解。
https://i-blog.csdnimg.cn/blog_migrate/6e2549402543db13f80fcc24535bf607.png
@SpringBootApplication 是一个方便的注解,它是 @SpringBootConfiguration 和 @EnableAutoConfiguration 的组合。这个注解让我们快速地开始一个 Spring Boot 项目。


[*]@SpringBootConfiguration:该注解表示当前类是一个 Spring Boot 的配置类,它会包含一些根本的 Spring Boot 配置。
[*]@EnableAutoConfiguration:该注解用于告诉 Spring Boot,根据当前项目中的类路径以及已经配置的属性,自动地选择和配置必要的 Bean。这大大简化了新项目标设置。
当你在 Spring Boot 项目中使用 @SpringBootApplication 注解时,它会自动配置一个开辟环境,以便你可以快速启动并测试你的应用程序。同时,它也提供了一个根本的目录结构,让你可以更轻易地管理你的代码。
也就是说 @SpringBootApplication 注解的类是 Spring MVC 项目标启动类,项目启动就是从这个类启动的。
我们先写一个简单的 Spring MVC 代码。
package com.example.springmvcdemo2;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("/hi")
    public String hi() {
      return "你好,Spring MVC";
    }
}
写完这段代码启动之后,控制台会出现下面这样的结果。
https://i-blog.csdnimg.cn/blog_migrate/d862d9511156de5bddd274291b95b4a5.png
出现这样的结果就表示我们的 Spring MVC 项目启动乐成。
然后我们就可以使用浏览器访问当前的 Spring MVC 项目,在浏览器搜刮栏输入http"//127.0.0.1:8080/hi,为什么要这样输入 URL 呢?前面我们说过,Spring 是在 servlet 的根本上开辟的,所以 Spring 使用的也是 tomcat 服务器,tomcat 默认绑定的是 8080 端口和 8005 管理端口,所以访问 Spring 项目也就必要访问我们电脑的 8080 端口,但是前面我们学习servlet的时候,会发现 URL 中除了有/hi之外,还会有一个项目名称在路径中,但是这里访问 Spring MVC 项目标时候为什么不必要在 URL 中添加项目名称呢?前面的 servlet 中,tomcat和项目之间的关系是:一个 tomcat 服务器下面可以有多个项目,而在 Spring MVC 中,tomcat 和项目之间的关系是:一个 Spring MVC 项目下含有一个 tomcat 服务器,也就是一个项目对应一个 tomcat 服务器,换句话说就是:Spring MVC 允许同一时间启动多个 tomcat 服务器,只要保证这多个 tomcat 服务器的端口不辩论就可以,所以我们在访问 Spring MVC 项目标时候就不必要指定项目名称。
然后 /hi 就是我们 @RequestMapping 中的内容。
https://i-blog.csdnimg.cn/blog_migrate/02f4a0383390f4ba8c4cd4f31f6c64c8.png
上面的简单代码中也出现了两个新的注解 @RestController 和 @RequestMapping。我们来看看这两个注解的作用。
2. @RestController

⼀个 Spring MVC 项⽬中,会有很多类,每个类大概有很多的⽅法,Spring程序怎么知道要执⾏哪个⽅法呢?
Spring会对所有的类进⾏扫描,假如类加了注解 @RestController,Spring才会去看这个类⾥⾯的⽅法有没有加 @RequestMapping 这个注解,当然他的作⽤不⽌这⼀点,咱们先⽤,后⾯再详细讲。
3. @RequestMapping

在 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作⽤。
路由映射:当用户访问⼀个 URL 时, 将⽤⼾的哀求对应到程序中某个类的某个⽅法的过程就叫路由映射。
我们在上面的代码中,当访问了 127.0.0.1:8080 ,也就是本地的 tomcat 服务器之后,后面的 /hi 就表示访问 /h1 路由映射的方法。
那么这个 @RequestMapping 中的 / 可以省略吗?其实是可以的,当你省略了这个 / 的时候,Spring 会为我们自动加上 / ,所以还是建议各人加上这个 /。
package com.example.springmvcdemo2;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("hi")
    public String hi() {
      return "你好,Spring MVC";
    }
}

https://i-blog.csdnimg.cn/blog_migrate/1b46ddac0c4547a7eee195f053db6e93.png
@RequestMapping 是 Spring MVC 项目中最常用到的注解之一,下面我们来详细学习一下 @RequestMapping 的使用。
3.1 @RequestMapping 使用

@RequestMapping 注解不光可以使用在方法上,还可以使用在类上。
package com.example.springmvcdemo2;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("sayHi")
public class HelloController {
    @RequestMapping("hi")
    public String hi() {
      return "你好,Spring MVC";
    }
}

当 @RequestMapping 修饰了类之后,那么访问的地点就是 类路径 + 方法路径了。
这里访问的时候,没有加上类路径,就无法乐成访问。
https://i-blog.csdnimg.cn/blog_migrate/c5c75c7e64f4fe64e2ac49bca1da1dd5.png
https://i-blog.csdnimg.cn/blog_migrate/9eba54501865655468dc3687f8f625a3.png
3.2 @RequestMapping 能接受 GET 方法还是 POST 方法

为了解决这个题目,我们必要使用 postman 来分别构造出 GET 哀求和 POST 哀求。
https://i-blog.csdnimg.cn/blog_migrate/78b5cb1a90b16fb913d37c96208756a0.png
https://i-blog.csdnimg.cn/blog_migrate/ce727201361f54b421fbf487c12ac290.png
通过 postman 构造出 GET 哀求和 POST 哀求可以看出来,这个 @RequestMapping 默认是支持 GET 方法和 POST 方法的,那么是否有方法可以是它只能接收 GET 大概 POST 哀求呢?答案是可以的。我们可以看看 @ReqeustMapping 的源码。
https://i-blog.csdnimg.cn/blog_migrate/5218206e35d72416dfe6824c877b2ffc.png

[*]@Target({ElementType.TYPE, ElementType.METHOD}): 这个注解可以用于类级别或方法级别。
[*]@Retention(RetentionPolicy.RUNTIME): 这个注解在运行时有用,即运行时的代码可以访问这个注解的信息。
[*]@Documented: 这是一个尺度的JavaDoc注释,用于生成API文档。
[*]@Mapping: 这个注解通常与Spring的@Configuration类一起使用,用于映射处理方法到URL路径。
[*]@Reflective({ControllerMappingReflectiveProcessor.class}): 这个注解指示Spring在运行时解析和处理带有此注解的类和方法。
注解中的各个元素:

[*]name(): 返回一个字符串,表示这个映射的名称。假如没有指定,默以为空字符串。
[*]value(): 返回一个字符串数组,表示必要映射的URL路径。假如没有指定,默以为空数组。这个路径可以包含占位符,例如/users/{id}。
[*]path(): 另一个用于指定URL路径的数组。这个数组的值和value()返回的值是一样的,它们是互为别名的。
[*]method(): 返回一个RequestMethod数组,表示这个映射接受的HTTP方法范例。假如没有指定,默以为空数组,表示接受所有方法范例。
[*]params(): 返回一个字符串数组,表示哀求参数。假如哀求参数符合数组中的任何一个,那么这个映射就会被触发。
[*]headers(): 返回一个字符串数组,表示哀求头。假如哀求头符合数组中的任何一个,那么这个映射就会被触发。
[*]consumes(): 返回一个字符串数组,表示这个映射接受的媒体范例。假如没有指定,默以为空数组,表示接受所有媒体范例。
[*]produces(): 返回一个字符串数组,表示这个映射能产生的媒体范例。假如没有指定,默以为空数组,表示能产生所有媒体范例。
@RequestMapping 中 method 默以为接收所有方法的哀求,假如我们想指定一种方法,就可以显式的指明哀求的方法。
@RequestMapping(value = "sayHi", method = RequestMethod.GET)
当 @RequestMapping 中只有一个参数的时候,这个参数会被以为是 value,假如有多个参数,则必要显式的指明参数的 key,雷同value=”sayhi“,method=RequestMethod.GET。
更改完 @RequestMapping 中的参数,只允许接收 GET 哀求之后,我们再用 postman 构造 GET 哀求和 POST 哀求看看结果。
https://i-blog.csdnimg.cn/blog_migrate/91a19c5f5932915d59baf5e5ab1e76a9.png
https://i-blog.csdnimg.cn/blog_migrate/c1034d6dba7e3ba1e0b764afe0c2396e.png
当构造出 POST 方法之后,就体现 405,方法不被允许。
4 哀求

访问不同的路径,就是发送不同的哀求。在发送哀求的时候,大概会通报一些参数,我们后端就必要拿到这些通报的参数来做出相应的业务处理。
4.1 通报单个参数

在 Spring MVC 中,我们可以使用跟通报来的参数相同的名字就可以获取到这个参数,这都是 Spring MVC 底层都帮我们做好了的,其实 Spring MVC 底层为我们做了很多事情,发送来的哀求在由 Controller 转发到 Mudel 的时候,一些我们大概必要用到的东西,都是 Spring MVC 底层帮我们处理过的,假如想要使用只必要拿到就可以了。
package com.example.springmvcdemo2;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/request")
public class UserController {
    @RequestMapping("/r1")
    public String r1(String name) {
      return "name: " + name;
    }
}
https://i-blog.csdnimg.cn/blog_migrate/2853c618247c84f1c0faa33fb6ffcba9.png
但是假如我们将参数的名字和哀求的参数名字不一样的话,那么我们后端这里就不能获取到这个参数。
package com.example.springmvcdemo2;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/request")
public class UserController {
    @RequestMapping("/r1")
    public String r1(String username) {
      return "name: " + username;
    }
}
https://i-blog.csdnimg.cn/blog_migrate/6755b7b25e43830f21ee71d429ffd25f.png
4.2 通报多个参数

@RequestMapping("/r2")
public String r2(String name, int age) {
    return "name: " + name + " age: " + age;
}
https://i-blog.csdnimg.cn/blog_migrate/ae1d21389d97d27b78e7d64c604e5860.png
可以发现,哀求中参数的序次和我们代码获取参数的序次可以不用保持同等。
假如我们这里的 age 没有通报参数的话,会发生什么呢?
https://i-blog.csdnimg.cn/blog_migrate/aed16bbae892116c0ba5af1e7990f1fe.png
当 int 范例的 age 参数为通报的话,这里会报出一个 500 的错误,也就是我们的服务器,Java代码出现了错误,这时就必要我们去看看代码日志,看看出现了什么题目。
https://i-blog.csdnimg.cn/blog_migrate/472915aa4cd15de1e142e1b9fb15e118.png
通过前面观察代码可以知道,假如参数未通报的话,会默认返回 null,但是由于我们接收参数使用的是 int 范例来接收这个参数的,int 范例不可以或许转换 null,所以就会报错,制止这个题目标方法就是使用 int 范例的包装类 Integer,这样就可以或许处理返回的 null。
@RequestMapping("/r2")
public String r2(String name, Integer age) {
    return "name: " + name + " age: " + age;
}
https://i-blog.csdnimg.cn/blog_migrate/b4ce8cff35988c37b74f3572e199e929.png
通过上面的一个例子,我们得出一个结论:在接收参数的时候,尽量使用根本数据范例的包装类。
另有一个题目,就是假如我们后端觉得这个参数是 Integer 范例,所以就用 Integer 来接收,但是哀求中该参数通报的却是 String 范例,那么这会发生什么题目呢?来看看。
https://i-blog.csdnimg.cn/blog_migrate/55689b408e54c8a61fc11331e970fe52.png
后端获取参数的数据范例必要和哀求的参数数据范例之间可以或许转换,否则就会出现题目,这就必要前端和后端协商好。
4.3 通报对象

假如通报的参数很多的时候,那么方法声明就必要很多的的形参,在后续增长参数的时候,也必要更改方法中的形参 ,所以这样就会显得很贫苦,所以我们就可以把这些参数封装为一个对象,可以在这个对象中一次性把所有大概会用到的参数都给写进去,假如哀求通报的参数没有的话,就会被设置为默认值,就算后续必要添加参数的话,也是在我们封装的这个对象中更改。当Spring MVC 接收的参数是对象范例的话,那么Spring MVC 就会根据对象中的属性,然后在哀求中找到对应的值,而且将这些属性给赋值,这样就极大的方便了我们程序员的工作。
封装一个 UserInfo 对象。
package com.example.springmvcdemo2;

public class UserInfo {
    private String name;
    private Integer age;
    private Integer id;

    public String getName() {
      return name;
    }

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

    public Integer getAge() {
      return age;
    }

    public void setAge(Integer age) {
      this.age = age;
    }

    public Integer getId() {
      return id;
    }

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

    @Override
    public String toString() {
      return "UserInfo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}
@RequestMapping("/r3")
public String r3(UserInfo user) {
    return user.toString();
}
https://i-blog.csdnimg.cn/blog_migrate/74e5e196568852778070e9fcd0e08575.png
https://i-blog.csdnimg.cn/blog_migrate/c2efbffda689fc6bebb570504c6dddb1.png
4.4 参数重定名

固然在做项目之前,前端和后端肯定协商了哀求中参数的名称,但是我后端程序员写的写的感觉这个名字欠好,我想换一个可以吗?答案是可以的,这里必要使用到 @RequestParam 注解来实现参数的重定名。
@RequestMapping("/r4")
public String r4(@RequestParam("name") String username, @RequestParam("age") Integer userage) {
    return "username: " + username + " userage: " + userage;
}
https://i-blog.csdnimg.cn/blog_migrate/019bad8a358cd26f4faf68dcc6d1eab1.png
假如我们加了这个注解之后,但是哀求中没有这个参数的时候就会出现题目了。
https://i-blog.csdnimg.cn/blog_migrate/2e6c3d824f8be7f946d9b31387d6bbcd.png
这是由于该注解默认该参数是必须要通报的,所以就必要设置该默认值为非必传。
https://i-blog.csdnimg.cn/blog_migrate/34bc7f5393337a59e440423f0ea4f4dc.png
@RequestMapping("/r4")
public String r4(@RequestParam("name") String username, @RequestParam(value = "age", required = false) Integer userage) {
    return "username: " + username + " userage: " + userage;
}
https://i-blog.csdnimg.cn/blog_migrate/9c15678101f7c27625e5b44b554bf726.png
4.5 通报数组

Spring MVC 可以自动绑定命组参数的赋值。也就是说:Spring MVC 会自动帮我们处理哀求参数是数组的情况。
@RequestMapping("/r5")
public String r5(String[] arr) {
    return Arrays.toString(arr);
}
https://i-blog.csdnimg.cn/blog_migrate/c89fa95365e5baf5312a00c26cb957f6.png
https://i-blog.csdnimg.cn/blog_migrate/e13643d1c8a65f0d578afd2bf2293bd7.png
4.6 通报集合

和通报数组雷同,当哀求中的参数有多个相同的名称时,也可以看作是集合,只不过假如将他看作是集合的话,必要使用 @RequestParam 绑定参数关系。由于在默认情况下,哀求中有多个参数名称相同的话,Spring MVC 会将它们封装为数组,要想封装为集合的话就必要使用 @RequestParam 来绑定参数关系。
@RequestMapping("/r6")
public String r6(@RequestParam("list") List<String> list) {
    return "size: " + list.size() + " list: " + list;
}
https://i-blog.csdnimg.cn/blog_migrate/e256e252808fe43c3a723126acbefaa1.png
4.6 通报JSON数据

JSON数据格式由于其易于阅读和编写、跨平台、跨语言、、轻量级等优势称为网络传输中最常使用的一种数据格式,那么在 Spring MVC 中如何接收到哀求中通报来的 JSON 数据格式呢?
由于 JSON 数据格式是键值对的情势,所以我们将必要接收的参数封装到 Java 对象中,然后 Spring MVC 参数中使用 @RequestBody 来修饰这个参数,这样当 Spring MVC 接收到 JSON 数据的时候,就会根据 Java 对象中的属性然后在这些 JSON 数据中查找相同名称的键值对,而且将值赋值给 Java 对象属性。
@RequestMapping("/r7")
public String r7(@RequestBody UserInfo user) {
    return user.toString();
}
https://i-blog.csdnimg.cn/blog_migrate/c8bbd29684c1185b3aae04abdc269254.png
fiddler 抓包结果。
https://i-blog.csdnimg.cn/blog_migrate/0ac18083bc8e6ad6a75a37525ec21cf0.png
4.7 获取URL中参数

通过使用 @PathVariable ,可以实现 URL 路径上的数据绑定。在路径上使用 {} 来表示要绑定的数据。
@RequestMapping("/r8/{name}/{age}")
public String r8(@PathVariable String name, @PathVariable Integer age) {
    return "name: " + name + " age: " + age;
}
https://i-blog.csdnimg.cn/blog_migrate/dad89e0de9be41ba76a8e86c964b87ac.png
Spring MVC 接收绑定命据的参数的时候,必要保证和哀求中通报的参数序次保持同等,否则就大概会出现题目。
https://i-blog.csdnimg.cn/blog_migrate/78afa18e3fe2f197d3dd1509a923dc05.png
被 @PathVariable 修饰的参数也是必传的,假如想要设置为非必传的话,不光必要更改 @PathVariable 的默认值,还必要在路径上做出调整。
我们先是只更改 @PathVariable 的默认设置看看能不能乐成。
@RequestMapping("/r8/{name}/{age}")
public String r8(@PathVariable(value = "name", required = false) String name, @PathVariable Integer age) {
    return "name: " + name + " age: " + age;
}
https://i-blog.csdnimg.cn/blog_migrate/c2f184f59ef9e708415adc1e66cdf1db.png
显然不能乐成,所以就还必要在路径上做出修改。
@RequestMapping({"/r8/{name}/{age}", "r8/{age}"})
public String r8(@PathVariable(value = "name", required = false) String name, @PathVariable Integer age) {
    return "name: " + name + " age: " + age;
}
https://i-blog.csdnimg.cn/blog_migrate/983d6d16a35b0575bef51052d9e1641b.png
4.8 上传文件

Spring MVC 同样可以接收到哀求传来的文件,使用 @RequestPart 修饰即可接收文件。
@RequestMapping("/r9")
public String r9(@RequestPart MultipartFile file) throws IOException {
    String fileName = file.getOriginalFilename();
    file.transferTo(new File("D:/tmp/" + fileName));
    return "接收到的文件的名称为" + fileName;
}
在 Spring MVC 中表示文件这种范例必要使用到 MultipartFile,transferTo()方法是将文件复制到指定的目录下。
postman 这样通报文件。
https://i-blog.csdnimg.cn/blog_migrate/088b8787a76a51cc2c348163bbb3e6b9.png
https://i-blog.csdnimg.cn/blog_migrate/b5736a256ae281f11e1fa284239b83bb.png
https://i-blog.csdnimg.cn/blog_migrate/15802c1eb5a29734df91d47be46f5124.png
4.9 获取cookie 和 session

获取cookie
前面我已经为各人介绍了关于 cookie 和 session 相关的知识,假如各人忘记了的话可以去看看,cookie 和 session。
由于 cookie 是由浏览器在发送哀求的时候带上的,所以我们就必要得到 HttpServletRequest 类,然后通过这个类来获取到 cookie。
@RequestMapping("/r10")
public String r10(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    StringBuilder sb = new StringBuilder();
    for (Cookie cookie : cookies) {
      sb.append(cookie.getName());
      sb.append("=");
      sb.append(cookie.getValue());
      sb.append("<br>");
    }

    return sb.toString();
}
写完 Spring MVC 代码之后,我们就必要在 postman 中构造 cookie 了。
https://i-blog.csdnimg.cn/blog_migrate/8e7ed59c769452ac97484db6d9b7527c.png
https://i-blog.csdnimg.cn/blog_migrate/100d4d5e02934c420c05b42b2d954f3f.png
点击 add domain 之后,就会出现下面的页面,然后我们点击 add cookie,添加cookie。
https://i-blog.csdnimg.cn/blog_migrate/f606082eab002f00cf4e3566530b3483.png
https://i-blog.csdnimg.cn/blog_migrate/f6cd55fefafd2e5a0565a73faefda835.png
这里 username 就表示的是这个 cookie 的 name,而 zhangsan 则表示该 cookie 的 value,Path 界说了Web站点上可以访问该 cookie 的目录,这里 / 表示所有目录。
https://i-blog.csdnimg.cn/blog_migrate/c3d16b1f5aa80a2e432c783a5d976a84.png
构造完成之后,在 postman 的这个部门就会显式我们设置好的cookie。
https://i-blog.csdnimg.cn/blog_migrate/1ce40fdad0a5ded5741f6963a296af02.png
https://i-blog.csdnimg.cn/blog_migrate/a33077884f9e6ff4b69cbbfb4a795aba.png
以上是一种获取 cookie 的方式,Spring MVC 还为我们提供了更方便的获取 cookie 的方式。
@RequestMapping("/r11")
public String r11(@CookieValue("username") String username) {
    return "username=" + username;
}
https://i-blog.csdnimg.cn/blog_migrate/54cb6a573f5ccabf6bf6e1c96a7d0ac4.png
但是这个方式一次只能获取到一个 cookie,假如我们必要获取到多个 cookie,就可以使用第一种获取 cookie 的方式。
获取session
咱们的服务器要想获取到 session,就必要依赖哀求中 cookie 中携带的 sessionId,然后在服务器这里服务器根据这个获取到的 sessionId,拿到与这个浏览器的 session,所以我们还是必要获取到 HttpServletRequest 对象。
在获取到 session 之前呢,还必要我们手动设置出一个 session,然后再设置一些属性。
@RequestMapping("/setsession")
public String setSession(HttpServletRequest request) {
    HttpSession session = request.getSession(true);
    session.setAttribute("username", "zhangsan");

    return "设置session完成";
}
https://i-blog.csdnimg.cn/blog_migrate/107670929c6863ade5832ee5bb15eaa4.png
当设置完成session之后,我们查察cookie,可以发现,cookie中自动添加进去了一个cookie,这个cookie就记录了sessionId。
https://i-blog.csdnimg.cn/blog_migrate/b2fd330eae40eac84464343c582ec908.png
设置完成 session 之后,我们就可以获取到这个 session 了。
@RequestMapping("/getsession")
public String getSession(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
      return "username: " + (String)session.getAttribute("username");
    }

    return "session为null";
}
https://i-blog.csdnimg.cn/blog_migrate/277d8b89af318e784e47a86a4ce5959f.png
Spring MVC 提供了几个简单的获取 session 的方式。
@RequestMapping("/getsession1")
public String getSession1(HttpSession session) {
    return "username: " + (String)session.getAttribute("username");
}
https://i-blog.csdnimg.cn/blog_migrate/7f86bb8b5d5974d214118fa3059b1102.png
不光如此,假如该浏览器和服务器是第一次创建连接,不存在 session ,也就是session为null的时候,session.getAtttribute() 不会出现空指针一样,这是由于 Spring MVC 底层帮我们解决了这个题目。
我们把这个sessionId cookie给删除了,然后给服务器发送哀求。
https://i-blog.csdnimg.cn/blog_migrate/f9529ecd0899a1ac17dea88304a1a771.png
https://i-blog.csdnimg.cn/blog_migrate/0080036799e69c907ae906ebee6709c7.png
另一种 Spring MVC 提供的方式更加简单,可以直接获取到 session 中的属性值。
@RequestMapping("/getsession2")
public String getSession2(@SessionAttribute("username") String username) {
    return "username: " + username;
}
https://i-blog.csdnimg.cn/blog_migrate/f708b46e59579e2cbcc9f0710743acea.png
4.10 获取 header

首先我们还是使用传统的方式获取到 header:通过 HttpServletRequest 类来获取到我们必要的header。
@RequestMapping("/getheader")
public String getHeader(HttpServletRequest request) {
    String ua = request.getHeader("User-Agent");
    return "获取到User-Agent:" + ua;
}
https://i-blog.csdnimg.cn/blog_migrate/d2196d367d650528d7fd241c6d3ee19b.png
https://i-blog.csdnimg.cn/blog_migrate/af1cff5759993f9de5fd2a15ea6eddb6.png
使用 Spring MVC 提供的 @RequestHeader 注解来获取到 header。
@RequestMapping("/getheader2")
public String getHeader1(@RequestHeader("User-Agent") String ua) {
    return "获取到User-Agent:" + ua;
}
https://i-blog.csdnimg.cn/blog_migrate/f73214efe689e8262738e76c0c6cc800.png
5. 响应

前面我们 Spring MVC 返回的响应都是数据范例,其实还可以返回一个静态页面、设置header、设置状态码等操纵。
5.1 返回静态页面

我们在这里写一个静态页面。
https://i-blog.csdnimg.cn/blog_migrate/1c9c32ab678d311abd82a6dac47c19f2.png
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    hello, spring mvc,这里是html页面
</body>
</html>
package com.example.springmvcdemo2;


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

import java.util.HashMap;
import java.util.Map;

@RequestMapping("/response")
@RestController
public class ResponseController {
    @RequestMapping("/index")
    public Object index() {
      return "/index.html";
    }
}
https://i-blog.csdnimg.cn/blog_migrate/14fc6bef704a77d4ffe02a2a2b1ed35e.png
https://i-blog.csdnimg.cn/blog_migrate/ead85ba14a5f04b1320a4c44670af141.png
通过 postman 构造哀求之后用 fiddler 抓包之后可以看到返回的是 text 范例的数据,而不是 html,也就是说我们的 Spring MVC 将“/index.htnl”当成了一个数据。那么这是为什么呢?
这是 @RestController 作用的结果,这个注解会默认将返回的都看作是数据,要想将返回的范例解析成 html 的话,就必要更换这个 @RestController 注解为 @Controller 。
package com.example.springmvcdemo2;


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

import java.util.HashMap;
import java.util.Map;

@RequestMapping("/response")
@Controller
public class ResponseController {
    @RequestMapping("/index")
    public Object index() {
      return "/index.html";
    }
}
https://i-blog.csdnimg.cn/blog_migrate/6afe3602c06b6727ea9a8c5dbcd2d4da.png
那么这个 @RestController 注解和 @Controller 注解有什么区别呢?我们来看看 @RestController 注解的源码。
https://i-blog.csdnimg.cn/blog_migrate/236d48267e4929ce6b4208a6a3855bc1.png
https://i-blog.csdnimg.cn/blog_migrate/bc87ad7c77d3723f367c816a1f65a155.png
https://i-blog.csdnimg.cn/blog_migrate/09ace1a25260d680b518e5952f51a57f.png
通过观察 @RestController 的源码我们可以发现,@RestController 注解可以写为 @Controller + @ResponseBody 。


[*]@Controller:界说一个控制器,Spring 框架启动时加载,把这个对象交给 Spring 管理
[*]@ResponseBody:界说返回的数据格式为非视图,返回一个 text/html 信息
5.2 返回数据

返回静态页面,我们使用 @Controller 注解,那么返回数据的话,我们只必要在 @Controller 注解的根本上加上 @ResponseBody 注解就可以了。所以我们的代码其实这样写更好,这样就可以使得一个类中的方法既可以返回视图也可以返回数据。
@RequestMapping("/response")
@Controller
public class ResponseController {

    @RequestMapping("/getstatus")
    @ResponseBody
    public String getStatus() {
      return "这返回的是一个数据";
    }
}
https://i-blog.csdnimg.cn/blog_migrate/5151938f0e0e97de0044c47f1c2e1fe1.png
https://i-blog.csdnimg.cn/blog_migrate/9e2e35960de8d73d5c7566ae87856ae0.png
5.3 返回HTML代码片段

在我们 Spring MVC 中返回的数据中假如存在HTML代码的时候,浏览器也会解析出这段HTML代码。
@RequestMapping("/returnhtml")
@ResponseBody
public String returnHtml() {
    return "<h1>Hello,HTML~</h1>";
}
https://i-blog.csdnimg.cn/blog_migrate/7a1050d55a5b612cdfc2db67daef49b3.png
通过 Fiddler 抓取 postman 发送的哀求,我们可以发现,响应的数据范例是 text/plain 纯文本范例,而不是 html 范例。
https://i-blog.csdnimg.cn/blog_migrate/0afc1baa06558712963a5798a807b0c5.png
而我们使用浏览器访问的话,使用 Fiddler 抓包可以观察到响应的数据的范例是 html 范例。
https://i-blog.csdnimg.cn/blog_migrate/81fcfd0689bfa2825fc5c349523a9362.png
这是为什么呢?其实这个取决于发送方能接收的数据范例,抓取 postman 哀求的数据包,可以发现 header 中的 accept 为 /,也就是可以接收所有范例的数据。
https://i-blog.csdnimg.cn/blog_migrate/7d21571e5c36f57f8a3b45b323d6fb3d.png
而且我们的 Spring MVC 代码中的返回范例写的是 String,也就是文本数据范例,所以使用 postman 发送哀求的话,响应的数据范例就是 text/plain 范例。
再观察浏览器发送哀求的数据包可以发现,accept 不是 /,而是指定了一些范例。
https://i-blog.csdnimg.cn/blog_migrate/8bcbb0ae66c659681c34aa5c6816bb57.png
而且浏览器能接收的数据范例中没有 text/plain,所以浏览器就会对返回的数据进行解析,看返回的数据可以解析成能接收的数据范例中的哪一种,由于我们返回的字符串是一个 html 片段,所以就会被解析为 html 范例。
样,我们还可以使用同样的方法返回CSS、JavaScript、json等范例。
5.4 返回JSON

Spring MVC 也可以返回 JSON 数据。
@RequestMapping("/returnjson")
@ResponseBody
public Map<String, String> returnJson() {
    Map<String, String> map = new HashMap<>();
    map.put("java", "java value");
    map.put("python", "python value");
    return map;
}
https://i-blog.csdnimg.cn/blog_migrate/d7903c636feed988b30ff771f0a7fb9f.png
https://i-blog.csdnimg.cn/blog_migrate/b263256538240e3ebc2c77815a6b1037.png
这里为什么返回的范例是 Map 范例,末了我们抓包抓取到的响应的数据范例还是 JSON 呢?
https://i-blog.csdnimg.cn/blog_migrate/b15730201c16a380d7437893c55377a0.png
通过 Fiddler 抓取到的哀求数据包中的 Accept 可以知道,当前接收任何范例的响应,也就是说,发送端没有明确的指出接收响应的数据范例,由于我们代码返回的数据范例是 Map,是一个对象,那么我们的 Spring MVC 注解@ResponseBody 会默认使用 Jackson 库对返回的数据范例进行序列化/反序列化,所以就会将我们返回的 Map 范例转换为 JSON 数据范例,而接收端能接收到任何范例的响应,所以接收到的数据范例就是 JSON 数据范例。
5.5 设置状态码

Spring MVC 设置状态码必要使用到 HttpServletResponse 类中的 setStatus() 方法。
@RequestMapping("/returnstatus")
@ResponseBody
public String returnStatus(HttpServletResponse response) {
    response.setStatus(401);
    return "设置状态码成功";
}
https://i-blog.csdnimg.cn/blog_migrate/a01192754dc2dcc932558836c23dbbd0.png
5.6 设置header

Spring MVC 设置状态码必要使用到 HttpServletResponse 类中的 setHeader() 方法。
@RequestMapping("/setheader")
@ResponseBody
public String setHeader(HttpServletResponse response) {
    response.setHeader("myheader", "666");
    return "设置header成功";
}
https://i-blog.csdnimg.cn/blog_migrate/80d327a1a27a7f916556d2a1343a43f9.png
https://i-blog.csdnimg.cn/blog_migrate/a612291b3a0cc7ce18142843f08f9a90.png
5.7 设置响应的Content-Type

Spring MVC 会根据我们返回的数据大概视图自动为我们设置相应的数据范例,当然我们也可以自己设置相应的数据范例。在 Spring MVC 中,设置响应中的数据范例必要更改 @ReqeustMapping 注解中的 produces 参数的值。
https://i-blog.csdnimg.cn/blog_migrate/a55c889e50df0e4e52c3e0a7b5c438d2.png


[*]name():指定注解的名称,默认值为空字符串。
[*]value() 和 path():这两个属性是互为别名的,它们指定了哀求映射的路径。假如未指定,默以为空数组。
[*]method():这个属性指定了哀求方法范例,例如 GET、POST、PUT 等。默以为空数组。
[*]params():这个属性指定了哀求参数,默以为空数组。
[*]headers():这个属性指定了哀求头信息,默以为空数组。
[*]consumes():这个属性指定了哀求内容范例,默以为空数组。
[*]produces():这个属性指定了响应内容范例,默以为空数组。
我们返回一个由键值对构成的字符串。
@RequestMapping(value = "/returnjson2", produces = "application/json; charset=utf8")
@ResponseBody
public String returnJson2() {
    return "{\"sucess\":true}";
}
https://i-blog.csdnimg.cn/blog_migrate/ff85b0d2e27e43eaf8c40e3e4fd59e74.png
https://i-blog.csdnimg.cn/blog_migrate/7dcbcc8283b72f50991491f9199714d4.png
假如我们不设置这个 produces 参数的,这个响应的字符串将会解析为文本范例。
https://i-blog.csdnimg.cn/blog_migrate/78c068f3a7ea99384f42650cedf735eb.png
https://i-blog.csdnimg.cn/blog_migrate/796bfb1f3b06ac30f29bca0ec1402268.png
Spring MVC 其实会根据你返回值的范例来决定是以什么范例进行响应的,但是对于哪些可以是文本范例又可以被解析为HTML、JavaScrip、CSS……等多种范例的时候就可以通过设置 produces 参数来指定响应的数据范例。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【SpringMVC】Spring Web MVC入门(一)