您现在的位置是:首页 >技术杂谈 >SpringMVC网站首页技术杂谈
SpringMVC
SpringMVC
1. SpringMVC定义
1.1. MVC定义
- Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
- View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
- Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
1.2. SpringMVC定义
文档:https://docs.spring.io/spring-framework/reference/
-
官方描述:Spring Web MVC is the original web framework built on the Servlet API and has been included inthe Spring Framework from the very beginning. The formal name,“Spring Web MVC,”comesfrom the name of its source module (spring-webmvc), but it is more commonly known as“Spring MVC”.
-
翻译:Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为“SpringMVC”。
由文档定义可见:SpringMVC是一个基于Servlet API构建的Web框架。
1.3. MVC和SpringMVC的关系
Spring MVC是基于MVC架构模式的Web框架,它是Spring框架的一部分。Spring MVC提供了一种结构清晰、可扩展和灵活的方法来开发Web应用程序。它采用了MVC设计模式中的分层架构,将应用程序的不同方面分离出来,并将其组织成相互关联的部分。
因此,我们可以说 MVC是一种思想,SpringMVC是对MVC思想的具体实现。
2. SpringMVC学习-创建
现在绝大部分的 Java 项目都是基于 Spring(或 Spring Boot)的,而 Spring 的核心就是 SpringMVC。
这里我们使用SpringBoot框架创建SpringMVC,只需要在创建勾选依赖的时候添加 SpringWeb模块依赖,这样我们的项目就是一个 SpringMVC框架的项目了。
3. SpringMVC学习-连接功能
3.0. 展示用户到 Spring 程序的互联互通
3.0.1.实现代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller//将类注入到Spring容器中
@ResponseBody//返回的数据不在是一个静态页面(不会去寻找一个静态页面)而是数据(text/html 或者 JSON数据)
@RequestMapping("/user")//此处的一级路由
public class UserController {
//路由器规则注册
@RequestMapping("/hi")//此处的二级路由
public String sayHi() {
return "<h1>Hi,Spring MVC.</h1>";//返回一个静态数据
}
}
3.0.2.访问网址
实现之后,启动项目。访问地址:http://localhost:8080/user/hi 时就能打印“hello,spring mvc”的信息了
解释:
- 8080 是SpringBoot项目的默认端口
- /user 是一级路径
- /hi 是二级路径
- @Controller 是将类注入到Spring容器中
- @ResponseBody 返回的数据不再是一个静态页面(不会去寻找一个静态页面)而是数据(text/html 或者 JSON数据)
3.1. @RequestMapping 注解介绍
@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的。上述代码中 此注解就用来注册了一级路由和二级路由的映射。
- 路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类 的某个⽅法的过程就叫路由映射(上述代码中将用户访问的请求对应到了程序中的 sayHi方法)
3.1.1. @RequestMapping 即可修饰类,也可以修饰⽅法
当修饰类和⽅法时,访问的地址是类 + ⽅法如上述示例代码,直接修饰方法时,直接访问方法路径即可。
直接修饰方法时:访问 http://localhost:8080/hi可获取信息
3.1.2. 关于@RequestMapping的请求方式
关于@RequestMapping的请求方式, 我们先使用Postman来测试下
使用Postman 我们可以观察到使用不同的请求方式都有返回结果,都是200返回值。说明这个注解默认情况下是支持多个请求方式的。
3.1.2.1. 多个请求方式
那到底都有哪些请求方式呢
进入到枚举类我们可以发现枚举的请求方式有GET,POST,PUT,DELETE等。说明当我们使用这个注解时,这些请求方式默认都是支持的。
3.1.2.2. 请求方式限制
如果我们不想支持多个注解怎么办呢,我们可以给注解增加参数(method)。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller//将类注入到Spring容器中
@ResponseBody//返回的数据不在是一个静态页面(不会去寻找一个静态页面)而是数据(text/html 或者 JSON数据)
@RequestMapping("/user")//此处的一级路由
public class UserController {
//路由器规则注册
//此时就只能通过post请求方式来访问了
@RequestMapping(value = "/hi", method = RequestMethod.POST)//此处的二级路由
public String sayHi() {
return "<h1>Hi,Spring MVC.</h1>";//返回一个静态数据
}
}
参数value是路由,另一个参数method就是请求方式。当我们这样设置时,就只能通过post请求来访问方法了。
使用Postman测试一下(当前只能POST请求方式)
GET方式请求
POST请求方式
可见当前方法的请求方式已经被限制了。
3.1.2.3. 注意:注解的method这个参数是一个数组,我们可以任意搭配请求方式
例如下代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/show", method = {RequestMethod.GET, RequestMethod.POST})
public String showRequestMapping() {
return "Mapping";
}
}
使用Postman访问测试,可以发现get方式和post方式都可以访问成功
3.1.2.4. 懒才是人类进步的阶梯
当然大家都是比较懒得,懒才是人类进步的阶梯。 所以大佬们也发明了比较简单的写法来让我们使用。看如下代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/hitoget", method = RequestMethod.GET)//此处只能使用Get方式请求
public String sayHiToGet() {
return "<h1>Hi,Spring MVC.</h1>";//返回一个静态数据
}
@GetMapping("/hello")//和上述同等效益
public String sayHello() {
return "<h1>Hello,Spring MVC.</h1>";//返回一个静态数据
}
}
使用Postman测试可以发现都是可以访问的
观察上述代码不难发现,就是以下两个注解是同等效益
- @GetMapping(“/hello”)
- @RequestMapping(value = “/hitoget”, method = RequestMethod.GET)
都是限制访问方式只能是Get方式。
为什么呢,让我们来看看源码
观察完我们不难发现同等效益的原因就是@GetMapping注解替我们完成了比较累的写法。所以我们可以轻松的实现Get请求功能。
当然其他的请求方式也有自己的简洁书写方式。
- get:@GetMapping
- post:@PostMapping
- put:@PutMapping
- delete:@DeleteMapping
注意:这些简洁注解无法组合使用,当同时使用时,只会对最上面的注解生效。使用时需要注意!(结论属于自我测试结果)
此处Get方式在最上面。
此处Post在最上面。
4. SpringMVC学习-获取参数
用户访问的时候会带⼀些参数,在程序中要想办法获取到参数
4.1. 获取单个参数
在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,例:在Controller层添加如下代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 获取单个参数
*/
@GetMapping("/get/par")
public String getParameter(String name) {
System.out.println(name);
return "<h1>Hi," + name + ".</h1>";
}
}
重启项目后我们通过Postman传递参数来观察
通过观察我们可以发现, 方法中参数名称为name,这是我们传递的name参数可以被接受到,但是如果我们使用age为名称来传递,后端是无法接收到参数的。所以我们在一般使用时,需要注意名称对应。
4.2. 获取多个参数
既然通过参数我们可以获取单个参数,那我们也可以通过相同的方法获取多个参数,例:在Controller层添加如下代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 获取多个参数
*/
@GetMapping("/get/pars")
public String getParameters(String name, String age, String address) {
System.out.println("大家好,我叫" + name + "。我今年" + age + "岁,来自" + address + "。");
return "大家好,我叫" + name + "。我今年" + age + "岁,来自" + address + "。";
}
}
重启项目后我们通过Postman观察,发现和获取单个参数同样的规律,就是传递的参数名称需要和后端方法中的参数名称相同,否则无法获取到数据。
当然有人会想:能否获取参数是不是和参数的顺序有关,那让我们用Postman来测试一下。
我们测试了两种方式,一:打乱参数,二打乱参数并添加干扰参数。两种方式,后端都接收参数成功,并且没有被然乱。这说明, 能否正常获取参数和参数顺序并没有关系。
4.3. 获取对象
获取对象的方式是将前端传来的数据根据名称,查询对象中相同名称的属性,然后赋值。(没有查询到并不会报错)
我们先构造一个类,注意:这个类需要有Setter方法。赋值会根据Setter方法赋值。
import lombok.Data;
@Data
public class Person {
private int id;
private String name;
private String age;
private String address;
}
同时在Controller层添加代码
import com.example.mvc_blog.vo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 获取对象
*/
@GetMapping("/get/obj")
public String getObject(Person echo) {
System.out.println(echo);
return echo.toString();
}
}
通过Postman来测试,接收数据成功。(只需要保证传递的参数名称和对象属性名称相同即可)
当然,我们也可以获取多个对象。(一般不会这么用)
添加新的类和Controller代码。
import lombok.Data;
@Data
public class Test {
private int id;
private String aa;
private String bb;
private String cc;
}
import com.example.mvc_blog.vo.Person;
import com.example.mvc_blog.vo.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 获取多个对象
*/
@GetMapping("/get/objs")
public String getObjects(Person echo, Test test) {
System.out.println(echo);
System.out.println(test);
return echo.toString() + test.toString();
}
}
我们可以观察到,Test类和Person类有一个相同属性 id。接下来我们访问Postman来观察效果。
通过Postman测试的结果,我们观察到,是可以同时接收多个对象的。只需要对应的属性名称相同。并且两个对象拥有相同属性名时,会同时赋值,不会报错。(仅展示结论,实际中并不会这么用)
4.4. 后端参数重命名
有时候,前端传给后端的参数不在我们的预期范围内。这个时候我们就可以用重命名将参数改为我们预期的名称。
4.4.1. 参数重命名
@RequestParam
注解就可以将我们的参数重命名。让我们编写代码来观察。(前端传递了一个名为 frontName 的参数,我们需要的是name的参数)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 前端传递了一个名为 frontName的参数,我们需要的是name的参数,
* 这个时候我们可以重命名
*/
@GetMapping("/rename")
public String rename(@RequestParam("frontName") String name) {
System.out.println(name);
return "<h1>Hi," + name + ".</h1>";
}
}
在上述代码中,Spring框架会通过@RequestParam
注解自动将请求参数frontName
的值赋值给方法的参数name
。(我们来通过Postman观察结果)
可以观察到成功获取了参数值,说明我们重命名成功。 那直接传递方法中的参数名name还有用吗? 让我们再来看看。
很明显, 使用@RequestParam
注解后,再使用方法中参数的名称是无法再传递成功的,只能使用注解内的参数(frontName)传递数据。
4.4.2. 参数设置必传
细心的朋友观察重命名后内容还会发现后端在提醒我们 需要前端的参数:frontName 。这是@RequestParam
注解存在一个参数–required,意为是否必须的。默认情况下这个参数是true,也就是必传的(不传递会报错400)。
如果我们不希望这个参数必传,只需将注解的required参数设置为false即可。(如下图)
将required设置为false后,不传递参数不会再报错了。
4.4.3. 参数设置默认值
@RequestParam
注解除了上述重命名和设置参数必传功能外,还可以使用defaultValue
属性来指定参数的默认值。例:在Controller层添加如下代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class TestController {
/**
* 默认值
*/
@GetMapping("/defa/value")
public String defa(@RequestParam(value = "frontName",
required = false,
defaultValue = "默认名称") String name) {
System.out.println(name);
return "<h1>Hi," + name + ".</h1>";
}
}
然后通过Postman测试并观察。
可以观察到我们没有传递参数时,属性会赋默认值。
这些就是@RequestParam
注解的基本使用。
4.5. 获取JSON对象
前面我们介绍了获取前端传来的参数的方式,那同样的方式能获取JSON数据吗?让我们编写代码来观察。
import com.example.mvc_blog.vo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
@PostMapping("/post/JSON")
public String postJSON(String body) {
System.out.println(body);
return "JSON:
" + body;
}
@PostMapping("/post/Per")
public String postPer(Person person) {
System.out.println(person);
return "JSON:" + person;
}
}
通过Postman我们可以观察到,使用之前的方法来接受JSON数据是不可行的(无论是普通参数,或者对象都是不可行的)。
这个时候,我们就需要一个新的注解:@RequestBody
注解,我们只需要在接受方法的参数前添加@RequestBody
注解,这个参数就会接受前端传来的JSON数据。让我们编写代码来观察。
import com.example.mvc_blog.vo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
/**
* 当接收方法中只有一个String类型接收时,会将前端传来的JSON数据转换为字符串。
*/
@PostMapping("/post/JSON")
public String postJSON(@RequestBody String body) {
System.out.println(body);
return "JSON:
" + body;
}
/**
* 当接受方法中的参数是一个对象时,注解会将获取到的JSON数据根据键值封装入对象中。
*/
@PostMapping("/post/Per")
public String postPer(@RequestBody Person person) {
System.out.println(person);
return "JSON:" + person;
}
}
代码中我们在接受JSON数据的方法参数前添加了 @RequestBody
注解。再次使用Postman访问观察。
可以观察到接受JSON数据成功,并且可以将接受的数据转换为对象。
解释
- 当接收方法中只有一个String类型接收时,
@RequestBody
注解会将前端传来的JSON数据原封不动的转换为字符串。 - 当接受方法中的参数是一个对象时,
@RequestBody
注解会将获取到的JSON数据根据键值封装入对象中。(键对应对象中的属性名时赋值)
4.6. 获取URL中参数
在Spring框架中,我们可以使用@PathVariable
注解来获取URL路径中的参数。
4.6.1. 获取单个参数
在Controller层添加如下代码,在路径中我们需要添加“/{name}”,然后在方法参数前添加注解 @PathVariable
。这样我们就可以在方法中获取到路径中的参数了。( 注意 {}中的名称需要和方法参数的名称对应 )
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
@GetMapping("/get/Url/{name}")
public String getUrl(@PathVariable String name) {
System.out.println(name);
return name;
}
}
通过Postman测试观察到,路径中的参数被获取成功。
4.6.2. 获取多个参数
在Controller层添加如下代码,同样在路径中我们需要添加“/{name}/{id}”,然后在方法参数前添加注解 @PathVariable
。这样我们就可以在方法中获取到路径中的多个参数了。
import com.example.mvc_blog.vo.Person;
import com.example.mvc_blog.vo.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
@GetMapping("/get/Urls/{name}/{id}")
public String getUrls(@PathVariable String name, @PathVariable String id) {
System.out.println(id + " : " + name);
return id + " : " + name;
}
}
可以观察到多级路径的参数获取同样可以成功。
注意
- {}中的名称需要和方法参数的名称对应,位置可以任意
- 路径中不可以少参数,例如 http://localhost:8080/get/Urls/echo 访问就会失败,因为没有绑定对应路径的方法
4.6.3. 正则表达式
除了上述的直接接受路径,我们还可以通过正则表达式来限制路径。例如下代码:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
/**
* 添加正则
*
* @param name 只接受大小写字母
* @param id 只接受数字
* @return
*/
@GetMapping("/get/Url/Re/{id:d+}/{name:[a-zA-Z]+}")
public String getUrlRe(@PathVariable String name, @PathVariable String id) {
System.out.println(id + " : " + name);
return id + " : " + name;
}
}
在正则表达式中,d
表示数字,[a-zA-Z]
表示匹配任意一个字母(不区分大小写),+
表示至少一位。 下来使用Postman测试观察。
由Postman测试可见,在上述代码中,我们使用了两个@PathVariable
注解分别将URL路径中的{id}
和{name}
参数绑定到方法的参数String id
和String name
上。同时,我们使用了正则表达式限制{id}
参数只能是数字,{name}
参数只能是字母。当客户端向服务器发起GET请求时, 如果URL路径中的参数不符合要求,将返回404错误 。
4.7. 获取文件
在Spring框架中,我们可以使用@RequestPart
注解来处理HTTP POST请求中的multipart/form-data请求体。也就是可以用来接收文件,并且Spring中封装了一个文件对象 MultipartFile
。具体使用如下。
4.7.1. 接受一个文件并保存
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 接受并保存一个文件
*
* @param uid:用户id
* @param file:接受的文件
* @return :返回接收结果
* @RequestPart("myFile"):接受文件的名称属性,需要和前端传递一致
* MultipartFile file:MultipartFile是SpringMVC提供简化上传操作的工具类。用于接收file
* <p>
* UUID : 全局唯一ID
*/
@RequestMapping("/upFile")
public String upFile(Integer uid, @RequestPart("myFile") MultipartFile file) {
//目录 配置文件解决 = imgPath
String imgPath = "D:/mixed/background/";//要存放文件的根路径
//图片名称 (时间戳 或 UUID) 每个人上传的名称不能重复
//获取原上传图片格式
String fileName = file.getOriginalFilename();//得到名称
fileName = fileName.substring(fileName.lastIndexOf("."));//得到文件后缀
fileName = UUID.randomUUID().toString() + fileName;
//保存文件到本地目录
try {
file.transferTo(new File(imgPath + fileName));
} catch (IOException e) {
//e.printStackTrace();
log.error("上传图片失败" + e.getMessage());
}
System.out.println(uid);
System.out.println("上传文件路径:" + imgPath + fileName);
return "上传文件路径:" + imgPath + fileName;
}
}
完成代码后使用Postman测试。
传递文件成功,并保存成功。
4.8. 获取Cookie
普通获取Cookie方式:使用 HttpServletRequest
获取到所有Cookie,然后查询需要的Cookie值。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Cookie 1.使用HttpServletRequest
* Servlet 原有方式
*/
@RequestMapping("/cookie")
public String getCookie(HttpServletRequest request) {
//得到全部的Cookie
Cookie[] cookies = request.getCookies();
String ret = null;
for (Cookie cookie : cookies) {
if ("echo".equals(cookie.getName())) {
ret = cookie.getValue();
break;
}
String s = "Cookie Name:" + cookie.getName() + " | Cookie Value: " + cookie.getValue();
log.info(s);
}
return "Cookie Value:" + ret;
}
}
通过Postman添加Cookie并访问,观察到获取Cookie成功。
上述为Servlet 原有的获取Cookie的方式。而SpringMVC为我们提供了更为简单的方式,那就是使用@CookieValue
注解。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Cookie 2.使用@CookieValue注解
* SpringMVC方式:方便,不需要再取所有的Cookie在寻找了
*/
@RequestMapping("/cookie2")
public String getCookie2(@CookieValue("echo") String cookie) {
return "Cookie Value:" + cookie;
}
}
上述代码中我们使用@CookieValue注解
来完成了获取Cookie的任务。 该注解这里的作用是将 Cookie 中Key为echo的值绑定到参数String cookie上。 相比于Servlet原有方式,很明显的代码简洁了许多。那注解有效果吗?让我们使用Postman来观察。
可以观察到我们使用@CookieValue注解
获取Cookie成功。
4.9. 获取Header
Header的获取和Cookie获取类似,都有 Servlet原有方式和SpringMVC注解 两种获取方式。我们先来看看Servlet原有获取方式。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Header 1.HttpServletRequest
* Servlet 原有方式
*/
@RequestMapping("/getua")
public String getHead(HttpServletRequest req) {
return "header: " + req.getHeader("User-Agent");
}
}
- 一般客户端浏览器通过 User-Agent 头部告诉服务器它的名称、版本号、操作系统以及其他一些浏览器相关的信息。
- 服务器通常会使用这些信息来识别不同的浏览器,并作出相应的响应。
- 所以这里 req.getHeader(“User-Agent”); 获取的就是客户端的一些信息。
使用Postman可以观察到获取Header信息成功。那再让我们试试SpringMVC中注解的方式。 这里我们可以使用 @RequestHeader
注解,该注解可以将指定名称的 Header 值绑定到参数上,从而方便地获取 Header 数据。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Header 2.@RequestHeader注解
* SpringMVC方式
*/
@RequestMapping("/getua2")
public String getHead2(@RequestHeader("User-Agent") String userAgent) {
return "header: " + userAgent;
}
}
使用Postman可以观察到我们使用@RequestHeader
注解获取Header同样成功。
4.10. 获取Session
Session获取方式和以上两种同样类似。都有 Servlet原有方式和SpringMVC注解 两种获取方式。
由于Session不好通过Postman模拟,这里我们使用后端先set一个Session,然后来获取。以下是存储Session代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 存储一个Session
* 存储的原因是因为无法模拟,所以Session相对于Cookie比较安全
*/
@RequestMapping("/setsess")
public boolean setSession(HttpServletRequest req) {
boolean result = false;
//得到HttpSession
HttpSession session = req.getSession(true);//true=如果没有则创建
//使用setAtt 设置值
session.setAttribute("userinfo", "userinfo");
result = true;
return result;
}
}
通过访问路径来让后端存储一个Session,这里可以看到存储成功了。
下来我们编写代码来获取Session,先使用Servlet原有方式来获取。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Session 1.
* 存储并取出session 1.HttpServletRequest
* Servlet 原有方式
*/
@RequestMapping("/getsess")
public String getSession(HttpServletRequest req) {
String result = null;
//得到HttpSession对象
HttpSession session = req.getSession(false);//false 默认不创建会话,只会去寻找是否存在,不存在则为空
//getAtt 得到Session 信息
if (session != null && session.getAttribute("userinfo") != null) {
result = (String) session.getAttribute("userinfo");
}
return result;
}
}
通过Postman观察到获取成功,让我们再来看看SpringMVC的注解方式。
SpringMVC中可以使用 @SessionAttribute
注解来绑定一个 session 属性到一个控制器处理方法的参数上。也就是以下代码中的方法参数 String userinfo
。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@Slf4j
public class TestController {
/**
* 获取Session 2.@SessionAttribute注解
* 此注解,默认是必须要传值的
* 可以使用 required = false (默认true)
* SpringMVC方式
*/
@RequestMapping("/getsess2")
public String getSession2(@SessionAttribute(value = "userinfo", required = false) String userinfo) {
return "会话:" + userinfo;
}
}
可以观察到使用@SessionAttribute
注解获取Session成功。需要注意的是 @SessionAttribute
注解默认此参数是必传参数,我们可以使用给注解设置参数: required = false
(默认为true,必传)。设置为false后 String userinfo
就是非必传参数了。
5. SpringMVC学习-返回数据
执行了业务逻辑之后,我们要把程序执行的结果返回给用户,所以在 SpringMVC 中我们就可以通过控制器方法返回不同类型的数据。
5.1. 返回静态页面
要返回静态页面,我们需要先书写一个静态页面。我们在static文件夹下创建一个名为 static_page.html的静态页面。具体内容如下。
<!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>静态页面</title>
</head>
<body>
<h1>欢迎来到鱼小飞的博客空间</h1>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class StaticController {
@GetMapping("/show/static/page")
public String showStaticPage() {
return "/static_page.html";
}
}
注意代码中,没有使用 @ResponseBody
注解。直接返回静态页面路径。此时就会通过视图处理器来处理返回结果
访问观察到返回静态页面成功。
5.2. 返回text/html
要返回 text/html 数据只需要在返回方法或者Controller类上添加上 @ResponseBody
注解即可。@ResponseBody
注解会将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端,返回结果不会再由视图处理器来处理。看代码示例
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
@RequestMapping("/ret/text/html")
public String retTextHtml() {
return "<h1>Hello,HTML~</h1>";
}
}
首先通过网页访问我们的路径,然后通过Fiddler抓包观察返回类型,就是我们要返回的 text/html类型。
5.3. 返回JSON对象
返回JSON对象和返回text/html 方式相同,同样是通过@ResponseBody
注解来封装返回类型。只需要返回值类型为key-value形式,@ResponseBody
注解就会将返回值封装为JSON格式。 看代码示例
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
public class TestController {
@RequestMapping("/ret/json")
public HashMap<String, String> retJSON() {
HashMap<String, String> map = new HashMap<>();
map.put("Java", "Java Value");
map.put("MySQL", "MySQL Value");
map.put("Redis", "Redis Value");
return map;
}
}
同上:通过网页访问我们的路径,然后通过Fiddler抓包观察返回类型,就是我们要返回的 JSON 类型。
5.4. 请求转发或请求重定向
return 不但可以返回⼀个视图,还可以实现跳转,跳转的方式有两种:
- 转发(forward):转发就是服务器内部访问新的页面,然后将数据展现给用户
- 所以:转发发生后,在前端页面的路径上是没有变化的
- 因为不是由前端访问的,所以前端路径还是在访问的上一级页面的路径
- 并且转发访问时一些静态资源路径是由当前位置起始寻找,所以可能出现静态资源访问不到的问题。使用时需要注意!
- 转发客户端和服务器只交互了一次
- 所以:转发发生后,在前端页面的路径上是没有变化的
- 重定向(redirect):重定向就是服务器给客户端一个新的访问路径,客户端重新向服务器发送一次请求
- 所以:重定向以后,前端页面的路径变为新的访问路径
- 重定向不会出现原来的外部资源不能访问的情况
- 重定向客户端和服务器交互了两次
下来看代码示例:
先在static文件夹下添加 hello.html 文件用于访问。
<!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>转发和重定向</title>
</head>
<body>
<h1>欢迎来到鱼小飞的博客空间</h1>
</body>
</html>
在Controller层添加请求转发的代码。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class NewController {
/**
* 请求转发实现 方式1:
* 转发:直接在服务器访问新地址,并返回数据
* forward
*/
@RequestMapping("/fw")
public String myForward() {
return "forward:/hello.html";
}
@RequestMapping("/fw1")
public String myForward1() {
//更简单的上述方式:省略forward:
return "/hello.html";
}
/**
* 请求转发实现 方式2:
* 转发:直接在服务器访问新地址,并返回数据
* forward
*/
@RequestMapping("/fw2")
public void myForward2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/hello.html").forward(req, resp);
}
}
访问页面可以观察到路径未发生变化。我们再来看看重定向
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class NewController {
/**
* 重定向实现 方式1:
* 重定向:返回给用户端新页面地址
* 用户端去访问新地址.两次访问
* redirect
*
* @return :
*/
@RequestMapping("/rd")
public String myRedirect() {
return "redirect:/hello.html";
}
/**
* 重定向实现 方式2:使用HttpServletResponse 的 sendRedirect 方法
* 重定向:返回给用户端新页面地址
* 用户端去访问新地址.两次访问
* redirect
*/
@RequestMapping("/rd2")
public void myRedirect2(HttpServletResponse resp) throws IOException {
resp.sendRedirect("/hello.html");
}
}
访问可以观察到最明显的区别就是路径发生了变化。
注意
在 Spring MVC 中,请求转发和请求重定向
通常用于返回 HTML 视图或者其他类型的文件,而 @ResponseBody
注解则通常用于返回 JSON 或者 XML 格式的数据。因此,请求转发和请求重定向一般不会和 @ResponseBody 同时使用。所以上述代码中并没有添加@ResponseBody
注解。
5.5. @ResponseBody 说明
@ResponseBody
注解主要会做两件事:
- 将返回值转换成JSON,如果返回值是String或者其他基本数据类型则不满足key-value形式,不能转换成json类型,则返回字符串给前端
- 设置响应头为application/json;charset=utf-8;若不能转换为json格式的则响应头设置为text/html
@ResponseBody 可以⽤来修饰方法或者是修饰类,修饰方法表示当前方法返回 html 或者 json,而不是视图。修饰类则表示类中的所有方法都会返回 html 或者 json,而不是视图。
5.6. 组合注解:@RestController
@RestController
注解是 Spring 框架中的一个组合注解,它同时包含了 @Controller
和 @ResponseBody
两个注解的功能。其中,@Controller
注解用于标识该类为控制器类,而 @ResponseBody
注解则用于将控制器方法的返回值转换成指定格式的数据并写入到响应体中返回给客户端浏览器。
当我们同时需要@Controller
和 @ResponseBody
两个注解的时候,就可以使用组合注解来节省书写代码的时间。
代码示例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @AUTHOR Echo
* @Version 1.0
* @Date 2023/5/28 1:40
* @ClassName CombinationController
* @deprecated exercises:组合注解示例
*/
@RestController
public class CombinationController {
@GetMapping("/showRest")
public String showRestController() {
//此时返回的非静态页面,没有被封装的话访问会出错
return "@RestController";
}
}
重启项目,访问来观察。
访问成功,说明组合注解生效。
6. 总结
对于 Spring MVC 来说,掌握了以上 3 个功能就相当于掌握了 Spring MVC。
如果想要了解更多注解,大家可以去查看官方文档:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-requestmapping.html
本篇文章到此结束。谢谢大家观看。