深入解析SpringMVC:从入门到精通
上文了解到SpingBoot的相关知识,此文开启Sping家族下的新篇章
1. 什么是SpringWebMVC?
官方对于SpringMVC的描述是这样的:
什么是Servlet呢?
Servlet 是⼀种实现动态页面的技术.准确来讲Servlet是⼀套JavaWeb开发的规范,或者说是⼀套Java
Web开发的技术标准.只有规范并不能做任何事情,必须要有人去实现它.所谓实现Servlet规范,就是真正编写代码去实现Servlet规范提到的各种功能,包括类、方法、属性等.
Servlet 规范是开放的,除了Sun公司,其它公司也可以实现Servlet规范,目前常见的实现了Servlet
规范的产品包括Tomcat、Weblogic、Jetty、Jboss、WebSphere等,它们都被称为 “Servlet 容器”.Servlet 容器用来管理程序员编写的Servlet类.
从上述定义我们可以得出⼀个信息:
SpringWebMVC是⼀个Web框架.
1.1 MVC定义
MVC是Model View Controller的缩写,它是软件工程中的⼀种软件架构设计模式,它把软件系统分为模型、视图和控制器三个基本部分
- View(视图)指在应用程序中专门用来与浏览器进行交互,展示数据的资源.
- Model(模型)是应用程序的主体部分,用来处理程序中数据逻辑的部分.
- Controller(控制器)可以理解为⼀个分发器,用来决定对于视图发来的请求,需要用哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即用来连接视图和模型
比如去饭店吃饭
客户进店之后,服务员来接待客户点餐,客户点完餐之后,把客户菜单交给前厅,前厅根据客户菜单给后厨下达命令.后厨负责做饭,做完之后,再根据菜单告诉服务员,这是X号餐桌客人的饭.
在这个过程中 服务员就是View(视图),负责接待客户,帮助客户点餐,以及给顾客端饭
前厅就是Controller(控制器),根据客户的点餐情况,来选择给哪个后厨下达命令.
后厨就是Model(模型),根据前厅的要求来完成客户的用餐需求
1.2 什么是SpringMVC?
MVC是⼀种架构设计模式,也是⼀种思想, 而SpringMVC是对MVC思想的具体实现.除此之外,Spring MVC还是⼀个Web框架.
总结来说,SpringMVC是⼀个实现了MVC模式的Web框架
在使用IDEA企业版的时候,创建SpingBoot项目后,我们勾选的依赖中就说明了SpringWeb框架其实就是SpringMVC框架
Spring Boot 可以添加很多依赖,借助这些依赖实现不同的功能.SpringBoot通过添加SpringWebMVC框架,来实现web功能
2. SpingMVC
因为是Web框架,所以当用户在浏览器中输入了url之后,我们的SpringMVC项目就可以感知到用户的请求,并给予响应.
SpingMVC通过浏览器和用户程序进行交互.
主要分以下三个方面:
- 建立连接: 将用户(浏览器)和Java程序连接起来,也就是访问⼀个地址能够调用到我们的Spring 程序。
- 请求: 用户请求的时候会带⼀些参数,在程序中要想办法获取到参数,所以请求这块主要是获取参数的功能.
- 响应: 执行了业务逻辑之后,要把程序执行的结果返回给用户,也就是响应.
比如用户去银行存款
- 建立连接:去柜台
- 请求:带着银行卡,身份证去存款
- 响应:银行返回⼀张存折.
2.1 项目准备
Spring MVC项目创建和SpringBoot创建项目相同,在创建的时候选择SpringWeb就相当于创建了Spring MVC的项目.
2.2 建立连接
在SpringMVC中使用@RequestMapping
来实现URL路由映射,也就是浏览器连接程序的作用
创建⼀个UserController类,实现用户通过浏览器和程序的交互,具体实现代码如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@RequestMapping("/hello")public String hello(){return "hello,my 1st SpingMVC project";}
}
2.2.1 @RequestMapping注解
@RequestMapping
是SpringWebMVC应用程序中最常被用到的注解之⼀,它是用来注册接口的路由映射的.
表示服务收到请求时,路径为/hello的请求就会调用sayHi这个方法的代码.
路由映射: 当用户访问⼀个URL时,将用户的请求对应到程序中某个类的某个方法的过程就叫路由映射.
既然@RequestMapping 已经可以达到我们的目的了,我们为什么还要加 @RestController呢?
我们可以试着把@RestController 去掉,再来访问⼀次:
可以看到,程序报了404,找不到该页面,这就是@RestController 起到的作用. ⼀个项目中,会有很多类,每个类可能有很多的方法,Spring程序怎么知道要执行哪个方法呢?
Spring会对所有的类进行扫描,如果类加了注解@RestController
,Spring才会去看这个类里面的方法有没有加@RequestMapping
这个注解,当然他的作用不止这⼀点
2.2.2 @RequestMapping使用
@RequestMapping既可修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类路径+方法路径.
@RequestMapping标识⼀个类:设置映射请求的请求路径的初始信息
@RequestMapping标识⼀个方法:设置映射请求请求路径的具体信息
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/luo")
public class UserController {@RequestMapping("/hello")public String hello(){return "hello,my 1st SpingMVC project";}
}
访问地址:http://127.0.0.1:8080/luo/hello
注意:
@RequestMapping
的URL路径最前⾯加不加/
(斜杠)都可以,Spring程序启动时,会进行判断,如果前面没有加/
,Spring会拼接上⼀个/
@RequestMapping
的URL路径也可以是多层路径, 最终访问时,依然是类路径+方法路径
2.2.3 @RequestMapping是GET还是POST请求?
我们来测试⼀下就知道结论了
GET请求:
浏览器发送的请求类型都是get,通过以上案例,可以看出来@RequestMapping
支持get请求
POST请求:
我们通过form表单来构造请求:
创建test.html, html代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>@RequestMapping test</title>
</head>
<body>
<form action="/luo/hello" method="post"><input type="submit" value="提交">
</body>
</html>
从运行结果可以看出:@RequestMapping 既支持Get请求,又支持Post请求.同理,也支持其他的请求方式.
那么在实际开发过程中,我们希望可以自定义是POST请求还是GET请求又或是别的操作,该如何操作呢?
2.3 Postman介绍
前言: 从上面的案例中,也发现了⼀个新的问题,就是我们测试后端方法时,还需要去写前端代码.这对我们来说,是一件麻烦又痛苦的事情.
随着互联网的发展,也随着项目难度的增加,企业也按照开发的功能,把人员拆分成了不同的团队.界面显示交给"前端开发工程师",业务逻辑的实现交给"后端开发工程师".
后端开发工程师,不要求也不需要掌握前端技能了. 那后端开发工程师,如何测试自己的程序呢?
这时,我们就需要使用专业的接口测试工具Postman
2.3.1 Postman下载
Postman下载,相关下载问题,可以查询相关博客了解,此处不做介绍
2.3.2 创建请求
2.3.3 界面介绍
2.3.4 传参介绍
-
普通传参:通过查询字符串来传参
前面的文章HTTP时,介绍了通过URL来访问互联网上的某⼀个资源时,URL的格式如下:
其中,查询字符串就是请求的参数
-
form-data:(完整表示为:multipart/form-data)
表单提交的数据, 在form标签中加上 enctyped="multipart/form-data
通常用于提交图片/文件.对应Content-Type: multipart/form-data
- x-www-form-urlencoded
form表单,对应Content-Type: application/x-www-from-urlencoded
- raw
可以上传任意格式的文本,可以上传text、json、xml、html
等
2.4 请求
前言: 我们访问不同的路径,就是发送不同的请求.在发送请求时,可能会带一些参数,所以Spring的请求,主要是如何传递参数到后端以及后端如何接收.
传递参数,主要是使用浏览器和Postman来模拟. 后端开发人员无需过度关注如何传递参数,了解即可,实际开发中以Postman测试为主.
比如餐厅的厨师,不关注用户是在店里下单,还是外卖平台下单,或者小程序下单,只需要知道如何接收订单,根据订单做出对应的事物就可以了.
2.4.1 传递单个参数
接收单个参数,在SpringMVC中直接用方法中的参数就可以,比如以下代码:
@RestController
@RequestMapping("/luo")
@RequestMapping("/test1")
public String t1(String name) {return "接受到参数name:"+name;
}
使用浏览器发送请求并传参
如果参数不⼀致,是获取不到参数的.
比如请求URL:http://127.0.0.1:8080/luo/test1?name1=yududu
响应结果:
2.4.1.1 注意事项:
使用基本类型来接收参数时,参数必须传(除boolean类型),否则会报500错误
类型不匹配时,会报400错误.
@RequestMapping("/luo/test1")public Object t2(int age){return "接收到参数age:" + age;}
1. 正常传递参数
http://127.0.0.1:8080/luo/test2?age=18
浏览器响应情况:
通过Fiddler观察请求和响应,HTTP响应状态码为200
,Content-Type
为text/plain
2. 不传递age参数
http://127.0.0.1:8080/luo/test2?
浏览器响应情况:
通过Fiddler观察请求和响应,HTTP响应状态码为500
观察程序的错误日志:
⼀般看日志堆栈信息的首行,
报错信息显示:
int类型的参数’age’,虽然为可选的,但由于被声明为基本类型而不能转换为空值.考虑将其声明为对应基本类型的包装类型.我们只需按照错误信息解决错误即可
3. 传递参数类型不匹配
http://127.0.0.1:8080/luo/test2?age=abc
浏览器响应情况:
通过Fiddler观察请求和响应, HTTP响应状态码为400
对于包装类型,如果不传对应参数,Spring接收到的数据则为null
所以建议在企业开发中,对于参数可能为空的数据,建议使用包装类型
2.4.2 传递多个参数
@RequestMapping("/test3")public String t3(String name,int age) {return "接受到参数name:"+name+"接受到参数age:"+age;}
使用浏览器发送请求并传参
http://127.0.0.1:8080/luo/test3?name=yududu&age=18
当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响后端获取参数的结果
比如访问http://127.0.0.1:8080/luo/test3?age=18&name=yududu一样的拿到相同的结果
2.4.3 传递对象
如果参数比较多时,方法声明就需要有很多形参.并且后续每次新增⼀个参数,也需要修改方法声明.所以我们建议把这些参数封装为⼀个对象
Spring MVC也可以自动实现对象参数的赋值,比如Person对象:
public class Person {public String name;public int age;public int password;public String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public int getPassword() {return password;}public void setPassword(int password) {this.password = password;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Person(String address, int password, int age, String name) {this.address = address;this.password = password;this.age = age;this.name = name;}public Person() {}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", password=" + password +", address='" + address + '\'' +'}';}
}
说明:
1. SpringMVC 默认是通过get和set方法来绑定参数的,不能靠构造方法进行参数绑定,所以需要提供get和set方法
2. 在不给参数的情况下必须存在无参构造方法,否则就必须传递所有参数
传递对象代码:
@RequestMapping("/test4")public String t4(Person person) {return person.toString();}
可以看到,后端程序正确拿到了Person对象里各个属性的值
Spring会根据参数名称自动绑定到对象的各个属性上,如果某个属性未传递,则赋值为null(基本类型则赋值为默认初识值,比如int类型的属性,会被赋值为0)
2.4.4 后端参数重命名(后端参数映射)
某些特殊的情况下,前端传递的参数key和我们后端接收的key可以不⼀致,比如前端传递了⼀个birthday给后端,而后端是使用day字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用@RequestParam
来重命名前后端的参数值.
不加@RequestParam注解的情况下:
@RequestMapping("/test5")public String t3(String birthday) {return "接受到参数birthday:"+birthday;}
加@RequestParam注解的情况下:
@RequestMapping("/test5")public String t3(@RequestParam("day") String birthday) {return "接受到参数birthday:"+birthday;}
可以看到,Spring可以正确的把浏览器传递的参数day绑定到了后端参数birthday参数上
1.
既然前后端参数绑定了,那么浏览器或者postman是否可以用后端参数birthday进行参数传递呢??
错误日志信息为:
控制台打印日志显示:请求参数’day’不存在2. 为什么不传递参数就报错呢??
可以看到required的默认值为true,表示含义就是:该注解修饰的参数默认为必传 此时只需将require的值改为false即可
@RequestMapping("/test5")
public String t3(@RequestParam(value = "day",required = false) String birthday) {return "接受到参数birthday:"+birthday;
}
此时我们发现可以被@RequestParam标记的注解不再是必传递参数了
总结:
1. 使用@RequestParam进行参数重命名时, 请求参数只能和使用@RequestParam 声明的名称⼀致,才能进行参数绑定和赋值.
2.@RequestParam 进行参数重命名时,参数就变成了必传参数.
2.4.5 传递数组
Spring MVC可以自动绑定数组参数的赋值
后端实现代码:
@RequestMapping("/test6")public String t6(String[] array) {return Arrays.toString(array);}
数组参数:请求参数名与形参数组名称相同且请求参数为多个,后端定义数组类型形参即可接收参数
请求方法如下多种:
-
http://127.0.0.1:8080/luo/test6array=yududu&array=luodudu&array=yuxiaomei&array=luoxiaoshuai
-
http://127.0.0.1:8080/luo/test6?array=yududu,luodudu,yuxiaomei,luoxiaoshuai
2.4.6 传递集合
集合参数: 和数组类似,同⼀个请求参数名有为多个,且需要使用@RequestParam
绑定参数关系,默认情况下,请求中参数名相同的多个值,是封装到数组.如果要封装到集合,要使用@RequestParam
绑定参数关系
错误示范:
@RequestMapping("/test7")public String t7(List<String> list) {return list.toString();}
不加注解就默认为数组,所以我们需要加上注解@RequestParam
正确示范:
@RequestMapping("/test7")public String t7(@RequestParam List<String> list) {return list.toString();}
2.4.7 传递JSON数据
2.4.7.1 JSON概念
JSON:JavaScript Object Notation 【JavaScript 对象表示法】
JSON是⼀种轻量级的数据交互格式.它基于ECMAScript(欧洲计算机协会制定的js规范)的⼀个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
简单来说:JSON就是⼀种数据格式,有自己的格式和语法,使用文本表示⼀个对象或数组的信息,因此JSON本质是字符串. 主要负责在不同的语言中数据传递和交换.
2.4.7.2 JSON的语法:
- 数据在
键值对(Key/Value)
中 - 数据由
逗号,
分隔 - 对象用
{}
表示 - 数组用
[]
表示 - 值可以为对象,也可以为数组,数组中可以包含多个对象
2.4.7.3 JSON的两种结构
- 对象:大括号
{}
保存的对象是⼀个无序的键值对集合.⼀个对象以左括号{
开始,右括号}
结束。每个"键"后跟⼀个冒号:
,键值对使用逗号,
分隔 - 数组:中括号
[]
保存的数组是值(value)的有序集合.⼀个数组以左中括号[
开始,右中括号]
结束,值之间使用逗号,
分隔。
以下就是一个简单的JSON文件:
{"sites": [{ "name":"菜鸟教程" , "url":"www.runoob.com" }, { "name":"google" , "url":"www.google.com" }, { "name":"微博" , "url":"www.weibo.com" }]
}
2.4.7.4 JSON字符串和Java对象互转
- JSON本质上是⼀个字符串,通过文本来存储和描述数据
- Spring MVC框架也集成了JSON的转换工具,我们可以直接使用,来完成JSON字符串和Java对象的互转
- 本质上是
jackson-databind
提供的功能,SpringMVC框架中已经把该工具包引入了进来,咱们直接使用即可,如果脱离SpringMVC使用,需要引入相关依赖 - JSON的转换工具包有很多,jackson-databind只是其中的⼀种.
- 使用ObjectMapper对象提供的两个方法,可以完成对象和JSON字符串的互转
writeValueAsString: 把对象转为JSON字符串
readValue: 把字符串转为对象
2.4.7.5 JSON优点
- 简单易用:语法简单,易于理解和编写,可以快速地进行数据交换
- 跨平台支持: JSON可以被多种编程语言解析和生成,可以在不同的平台和语言之间进行数据交换和传输
- 轻量级:相较于XML格式,JSON数据格式更加轻量级,传输数据时占用带宽较小,可以提高数据传输速度
- 易于扩展: JSON的数据结构灵活,支持嵌套对象和数组等复杂的数据结构,便于扩展和使用
- 安全性:JSON数据格式是⼀种纯文本格式,不包含可执行代码,不会执行恶意代码,因此具有较高的安全性
2.4.7.6 传递JSON对象
接受JSON对象,需要使用@RequestBody
注解
RequestBody: 请求正文,意思是这个注解作用在请求正⽂的数据绑定,请求参数必须在写在请求正文中
后端实现:
@RequestMapping("/test8")public String t7(@RequestBody Person person) {return person.toString();}
使⽤Postman来发送json请求参数:
如果去掉@RequestBody
注解,后端就获取不到值
2.4.8 获取URL中参数@PathVariable
path variable: 路径变量
和字面表达的意思⼀样,这个注解主要作用在请求URL路径上的数据绑定
默认传递参数写在URL上,SpringMVC就可以获取到
后端实现代码:
@RequestMapping("/test10/{id}/{name}")public String t10( @PathVariable int id ,@PathVariable("name") String username) {return "接受到参数id:"+id+",username:"+username;}
使用Postman发送请求
可以看到,后端正确获取到了URL中的参数
参数对应关系如下:
如果方法参数名称和需要绑定的URL中的变量名称⼀致时,可以简写,不用给@PathVariable的属性赋值,如上述例子中的id变量
如果方法参数名称和需要绑定的URL中的变量名称不⼀致时,需要@PathVariable的属性value赋值,如上述例子中的username变量.
2.4.9 上传文件@RequestPart
现在D盘下创建一个temp文件夹用于后续验证
后端代码实现:
@RequestMapping("/test11")public String t11( @RequestPart("file") MultipartFile file) throws IOException {//获取⽂件名称String fileName = file.getOriginalFilename();//⽂件上传到指定路径file.transferTo(new File("D:/temp/" + file.getOriginalFilename()));return " 接收到⽂件名称为: "+fileName;}
使用Postman发送请求
2.4.10 获取Cookie/Session
2.4.10.1 什么是Cookie?
首先我们知道HTTP协议自⾝是属于"无状态"协议.
"无状态"的含义指的是:
默认情况下HTTP协议下的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系.
但是实际开发中,我们很多时候是需要知道请求之间的关联关系的.
例如登陆网站成功后,第二次访问的时候服务器就能知道该请求是否是已经登陆过了
上述图中的 “令牌” 通常就存储在Cookie字段中.
比如去医院挂号
- 看病之前先挂号.挂号时候需要提供身份证号,同时得到了⼀张"就诊卡",这个就诊卡就相当于患者的"令牌".
- 后续去各个科室进行检查,诊断,开药等操作,都不必再出示身份证了,只要凭就诊卡即可识别出当前患者的身份.
- 看完病了之后,不想要就诊卡了,就可以注销这个卡.此时患者的身份和就诊卡的关联就销毁了.(类似于网站的注销操作)
- 又来看病,可以办⼀张新的就诊卡,此时就得到了⼀个新的"令牌"
此时在服务器这边就需要记录"令牌"信息,以及令牌对应的用户信息,这个就是Session机制所做的工作.
2.4.10.2 什么是Session?
会话: 对话的意思在计算机领域, 会话是⼀个客户端与服务器之间的不中断的请求响应.对客户端的每个请求,服务器能够识别出请求来自于同⼀个客户端.当⼀个未知的客户端向Web应⽤程序发送第⼀个请求时就开始了⼀个会话.
当客户端明确结束会话或服务器在一个时限内没有接受到客户端的任何请求时,会话就结束了. 比如我们打客服电话,每次打客服电话,是⼀个会话.挂断电话,会话就结束了 下次再打客服电话,又是一个新的会话. 如果我们长时间不说话,没有新的请求,会话也会结束.
服务器同一时刻收到的请求是很多的.服务器需要清楚的区分每个请求是属于哪个客户端,也就是属于哪个会话,就需要在服务器这边记录每个会话以及与用户的信息的对应关系.
Session是服务器为了保存用户信息而创建的⼀个特殊的对象
Session的本质就是⼀个"哈希表",存储了一些键值对结构.Key就是SessionID,Value就是用户信息(用户信息可以根据需求灵活设计)
SessionId 是由服务器⽣成的⼀个"唯⼀性字符串",从Session机制的角度来看,这个唯⼀性字符串称为"SessionId". 但是站在整个登录流程中看待,也可以把这个唯⼀性字符串称为 “token”.
上述例子中的令牌ID,就可以看做是SessionId,只不过令牌除了ID之外,还会带⼀些其他信息,比如时间,签名等
说明:
- 当用户登陆的时候,服务器在Session中新增⼀个新记录,并把sessionId返回给客户端.(通过HTTP响应中的Set-Cookie字段返回).
- 客户端后续再给服务器发送请求的时候,需要在请求中带上sessionId.(通过HTTP请求中的Cookie字段带上).
- 服务器收到请求之后,根据请求中的sessionId在Session信息中获取到对应的用户信息,再进行后续操作.找不到则重新创建Session,并把SessionID返回.
2.4.10.3 Cookie 和Session的区别
- Cookie是客户端保存用户信息的⼀种机制.Session是服务器端保存用户信息的⼀种机制
- Cookie和Session之间主要是通过SessionId关联起来的,SessionId是Cookie和Session之间的桥梁
- Cookie和Session经常会在⼀起配合使用.但不是必须配合.
- 完全可以用Cookie来保存⼀些数据在客户端.这些数据不⼀定是用户身份信息,也不一定是
SessionId - Session中的sessionId也不需要非得通过Cookie/Set-Cookie传递,比如通过URL传递
2.4.10.4 Cookie的获取
2.4.10.4.1 传统获取Cookie
@RequestMapping("/test12")public String t12(HttpServletRequest request, HttpServletResponse response) {//获取Cookies信息Cookie[] cookies = request.getCookies();//打印Cookie信息StringBuilder builder = new StringBuilder();if (cookies!=null){for (Cookie ck:cookies) {builder.append(ck.getName()+":"+ck.getValue());}}return "Cookie信息:"+builder;}
Spring MVC是基于Servlet API构建的原始Web框架,也是在Servlet的基础上实现的
HttpServletRequest ,HttpServletResponse
是Servlet提供的两个类,是SpringMVC⽅法的内置对象.需要时直接在⽅法中添加声明即可.
HttpServletRequest
对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息.HttpServletResponse
对象代表服务器的响应.HTTP响应的信息都在这个对象中,比如向客户端发送的数据,响应头,状态码等.通过这个对象提供的方法,可以获得服务器响应的所有内容Spring MVC在这两个对象的基础上进行了封装,给我们提供更加简单的使用方法.
此时没有设置Cookie,通过浏览器访问:http://127.0.0.1:8080/luo/test12,得到Cookie为null
我们设置⼀下Cookie的值
从这个例子中,也可以看出Cookie是可以伪造的,也就是不安全的,所以使用Cookie时,后端需要进行Cookie校验
2.4.10.4.2 简单获取Cookie
@RequestMapping("/getCookie")public String cookie(@CookieValue("name") String username) {return "name: " + username;}
2.4.10.5 Session存储与获取
Session是服务器端的机制,我们需要先存储,才能再获取
Session也是基于HttpServletRequest来存储和获取的
2.4.10.5.1 Session 存储
@RequestMapping("/setSession")public String setSession(HttpServletRequest request) {//获取Session对象HttpSession session = request.getSession();if (session != null) {session.setAttribute("yududu", "18");}return "session存储成功";}
这个代码中看不到SessionId这样的概念.getSession操作内部提取到请求中的Cookie里的SessionId, 然后根据SessionId获取到对应的Session对象, Session对象用HttpSession来描述
2.4.10.5.2 Session 获取
读取Session可以使用HttpServletRequest
@RequestMapping("/getSession")public String getSession(HttpServletRequest request) {//如果session不存在,不会自动创建,因为getSession()方法默认值为true,表示没有就创建,false表示没有就也不会自动创建HttpSession session = request.getSession(false);String username = null;if (session != null && session.getAttribute("username") != null) {username = (String) session.getAttribute("username");}return "username: " + username;}
Object getAttribute(String name)
返回在该session会话中具有指定名称的对象,如果没有指定名
称的对象,则返回null.
说明:
获取Session有两种方式
HttpSession getSession(boolean create);HttpSession getSession();
- HttpSession getSession(boolean create) :
参数如果为true,则当不存在会话时新建会话
参数如果为false, 则当不存在会话时返回null - HttpSession getSession(): 和getSession(true) 含义⼀样, 默认值为true.
- void setAttribute(String name, Object value):使用指定的名称绑定⼀个对象到该session会话
简洁获取Session:
@RequestMapping("/getSess2")public String sess2(@SessionAttribute(value = "username",required = false)
String username) {return "username:"+username;}//通过SpringMVC内置对象HttpSession来获取
@RequestMapping("/getSess3")public String sess3(HttpSession session) {String username = (String)session.getAttribute("username");return "username:"+username;}
2.4.11 获取Header
2.4.11.1 传统获取Header
@RequestMapping("/getHeader")public String getHeader(HttpServletRequest request) {String userAgent = request.getHeader("User-Agent");return "Header: " +userAgent;}
2.4.11.2 简洁获取Header
@RequestMapping("/getHeader2")public String getHeader2(@RequestHeader("User-Agent") String userAgent) {return "Header: " +userAgent;}
2.5 响应
2.5.1 返回静态页面
首先我们在目录结构下创建一个普通的
前端页面代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>从前有座山,山里有座庙
</body>
</html>
请求代码:
@RequestMapping("/index")public Object returnHtml() {return "/index.html";}
此时,http响应把"/index.html" 当做了http响应正文的数据,这是注解的问题
我们只需要把@RestController
——>@Controller
这是为何呢??
我们发现@RestController=@Controller+@ResponseBody
说明:
@Controller :
定义⼀个控制器,Spring框架启动时加载,把这个对象交给Spring管理.
@ResponseBody :
定义返回的数据格式为非视图,返回⼀个text/html信息
2.5.2 返回数据@ResponseBody
- @ResponseBody 注解,该方法就会把"/index.html"当做⼀个数据返回给前端.
- @ResponseBody 既是类注解,⼜是方法注解
如果作用在类上,表示该类的所有方法,返回的都是数据,如果作用在方法上,表示该方法返回的是数据. - 如果在类上添加 @ResponseBody就相当于在所有的方法上添加了@ResponseBody
- 如果⼀个类的方法里,既有返回数据的,又有返回页面的,就把 @ResponseBody 注解添加到对应的方法上即可
2.5.3返回HTML代码片段
后端返回数据时,如果数据中有HTML代码,也会被浏览器解析
@RequestMapping("/returnLittleHtml")@ResponseBodypublic String returnLittleHtml() {return "<h1>返回了部分代码片段</h1>";}
响应中的 Content-Type
常见取值有以下几种:
- text/html :body数据格式是HTML
- text/css :body数据格式是CSS
- application/javascript :body数据格式是JavaScript
- application/json :body数据格式是JSON
- 如果请求的是js文件,SpringMVC会自动设置Content-Type为application/javascript
2.5.4返回JSON
@RequestMapping("/returnJson")@ResponseBodypublic HashMap<String, String> returnJson() {HashMap<String, String> map = new HashMap<>();map.put("yududu", "111");map.put("luodudu", "222");map.put("xiaoxiaobin", "333");return map;}
2.5.5设置状态码
Spring MVC会根据我们⽅法的返回结果⾃动设置响应状态码,程序员也可以⼿动指定状态码
通过SpringMVC的内置对象HttpServletResponse提供的⽅法来进⾏设置
@RequestMapping(value = "/setStatus")@ResponseBodypublic String setStatus(HttpServletResponse response) {response.setStatus(401);return " 设置状态码成功 ";}
相关文章:
深入解析SpringMVC:从入门到精通
上文了解到SpingBoot的相关知识,此文开启Sping家族下的新篇章 1. 什么是SpringWebMVC? 官方对于SpringMVC的描述是这样的: 什么是Servlet呢? Servlet 是⼀种实现动态页面的技术.准确来讲Servlet是⼀套JavaWeb开发的规范,或者说…...
Java-Collections类高效应用的全面指南
Java-Collections类高效应用的全面指南 前言一、Collections 类概述二、Collections 类的基础方法2.1 排序操作2.1.1 sort方法2.1.2 reverse方法2.1.3 shuffle方法 2.2 查找与替换操作2.2.1 binarySearch方法2.2.2 max和min方法2.2.3 replaceAll方法 三、Collections 类的高级应…...
微软家各种copilot的AI产品:Github copilot、Microsoft copilot
背景 大家可能听到很多copilot,比如 Github Copilot,Microsoft Copilot、Microsoft 365 Copilot,有什么区别 Github Copilot:有网页版、有插件(idea、vscode等的插件),都是面向于程序员的。Mi…...
JAVA EE_HTTP
为什么意气风发的少年,总是听不进去别人的劝解。 ----------陳長生. ❀主页:陳長生.-CSDN博客❀ 📕上一篇:JAVA EE_网络原理_数据链路层-CSDN博客 1.HTTP 1.1.HTTP是什么 H…...
SEO 优化实战:ZKmall模板商城的 B2C商城的 URL 重构与结构化数据
在搜索引擎算法日益复杂的今天,B2C商城想要在海量信息中脱颖而出,仅靠优质商品和营销活动远远不够。ZKmall模板商城以实战为导向,通过URL 重构与结构化数据优化两大核心策略,帮助 B2C 商城实现从底层架构到搜索展示的全面升级&…...
使用HtmlAgilityPack采集墨迹天气中的天气数据
需要解析对应的HTML源码: <div class"left"><div class"wea_alert clearfix"><ul><li><a href "https://tianqi.moji.com/aqi/china/jiangxi/hukou-county" >< span class"level level_2&qu…...
广和通L610模块通过AT指令访问服务器方案:嵌赛使用
实现步骤及关键点: 网络连接配置 ATCGDCONT1,"IP","APN名称" // 设置APN ATCGACT1,1 // 激活PDP上下文 ATCGATT1 // 附着GPRS网络 HTTP协议支持验证 L610支持HTTP客户端功能,关键指…...
nodejs快速入门到精通1
参考 nodejs快速入门到精通 菜鸟教程-nodejs nodejs官方文档 原因 视频免费 资料收费 笔记还是自己写吧 安装 nodejs官网 windows下: #查看nodejs版本 node -v #查看npm版本 npm -v #设置npm为淘宝镜像源 npm config set registry https://registry.npmmirror.…...
Linux下软件安装
一、软件安装方式 在 Linux 系统中,常见的软件安装方式有以下几种: 包管理器安装(如 yum、apt) 这是最便捷的安装方式,通过系统自带的包管理器从官方软件源下载并安装软件。 # CentOS/RHEL系统使用yum安装 yum insta…...
React Fiber 架构深度解析:时间切片与性能优化的核心引擎
文章目录 前言一、Fiber 架构的诞生背景二、Fiber 架构的核心概念1. Fiber 节点2. Fiber 节点的结构3. 双缓存机制4. 增量渲染与优先级调度 三、时间切片技术详解1. 时间切片的定义与原理2. 时间切片与 JavaScript 事件循环3. 时间切片的实现方式4. 时间切片的优势 四、Fiber 架…...
mcp学习笔记
MCP(Model Context Protocol)是一种由Anthropic推出的开放协议,旨在统一大型语言模型(LLM)与外部数据源/工具之间的交互。其核心组件包括 MCP Client、MCP Server 和 Function Calling 机制,三者协…...
鸿蒙北向源码开发: 检查应用接口dts文件api规范性
开源鸿蒙5.0.2对应的api版本是14 5.0社区仓有工具检查接口规范性报告工具: interface/sdk-js/build-tools/api_check_plugin api_check_plugin是什么? 在解释api_check_plugin是什么之前得先知道 应用调用的api接口都是文件名后缀为.d.ts的文件,这些文件内部声明了arkts的a…...
Redis设计与实现——分布式Redis
Redis Sentinel(哨兵) Sentinel 的工作机制 故障检测(Failure Detection) 主观下线(Subjective Down):单个 Sentinel 实例检测到主节点在30 秒内无响应,标记其为 SDOWN。 客观下线…...
【Redis】Hash 哈希
文章目录 常用命令hsethgethmgethexistshkeyshvalshgetallhdelhlenhsetnxhincrbyhincrbyfloat 内部编码应用场景 Redis 存储键值对,也就是 key - value,不过同时也允许 value 也为键值对,但此时为了避免冲突,为 field - value PS…...
【matlab技巧】通过手绘的方法设计二维运动轨迹,附MATLAB程序
代码提供了一种直观的方式来使用鼠标在图形窗口中绘制线条,同时能够记录并显示用户绘制的轨迹坐标。用户通过左键点击绘制点,右键点击结束绘制,适合用于简单的图形交互和数据可视化。 文章目录 代码作用运行MATLAB源代码代码详解 代码作用 这…...
游戏引擎学习第290天:完成分离渲染
game_sim_region.cpp:在BeginSim中移除EntityOverlapsRectangle调用 现在我们接近一个关键点,虽然还没完全结束,但我们已经把所有东西迁移到了一个新概念上——即那些临时创建的控制器结构,称为“脑”(brains…...
uniapp-商城-61-后台 新增商品(添加商品到数据库)
完成商品的布局,完成商品的属性添加,最后的目的还是要完成数据添加,将我们前台的数据添加后台的数据库。 1、界面 2、点击提交完成商品添加 点击下方的提交按钮,将数据添加到数据库。 onSubmit 使用该函数---见3 <view cla…...
深入浅出IIC协议 - 从总线原理到FPGA实战开发 -- 第一篇:I2C总线协议深度解剖
第一篇:I2C总线协议深度解剖 副标题 : 两根线如何征服千亿设备?详解硬件工程师必须掌握的通信奥义 1. 为什么I2C仍是嵌入式经典? 1.1 总线拓扑的哲学 拓扑对比图 SPI需4线N片选 vs I2C仅2线级联 UART点对点 vs I2C多主从架构 成本控制实…...
QT之LayOut布局
文章目录 QFormLayoutQGridLayoutQFormLayout、QGridLayout、QHBoxLayout、QVBoxLayout综合案例用QFormLayout 代替 界面左边部分的QGridLayout QFormLayout #include "widget.h"#include <QFormLayout> #include <QLineEdit>Widget::Widget(QWidget *p…...
ubuntu 24.04安装ros1 noetic
为了后续能够找到安装方法,记录一下: 参考如下链接安装: https://www.reddit.com/r/ROS/comments/158icpy/compiling_ros1_noetic_from_source_on_ubuntu_2204/ 安装步骤如下: 1.获取apt 包 sudo apt-get install python3-ros…...
动态规划-64.最小路径和-力扣(LetCode)
一、题目解析 从左上角到右下角使得数字总和最小且只能向下或向右移动 二、算法原理 1.状态表示 我们需要求到达[i,j]位置时数字总和的最小值,所以dp[i][j]表示:到达[i,j]位置时,路径数字总和的最小值。 2.状态转移方程 到达[i,j]之前要先…...
Python爬虫(29)Python爬虫高阶:动态页面处理与云原生部署全链路实践(Selenium、Scrapy、K8s)
目录 引言:动态爬虫的技术挑战与云原生机遇一、动态页面处理:Selenium与Scrapy的协同作战1.1 Selenium的核心价值与局限1.2 Scrapy-Selenium中间件开发1.3 动态分页处理实战:京东商品爬虫 二、云原生部署:Kubernetes架构设计与优化…...
FauxGen:一款由 CodeBuddy 主动构建的假数据生成器
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在前端开发中,经常需要一些「假数据」来模拟真实接口,便于开发阶段的界面构建和功能测试…...
chrome 浏览器插件 myTools, 日常小工具。
1. 起因, 目的: 比如,chatgpt, google, 打开网页,就能直接输入文字,然后 grok 就不行,必须用鼠标点一下,才能输入文字。 对我而言,是个痛点!写个插件,自动点…...
从代码学习深度学习 - 词嵌入(word2vec)PyTorch版
文章目录 前言1. 为什么需要词嵌入?2. 早期尝试:独热向量 (One-Hot Vectors)独热向量的局限性3. 自监督的 word2vec4. 跳元模型 (Skip-Gram Model)4.1. 训练5. 连续词袋 (CBOW) 模型5.1. 训练总结前言 自然语言处理(NLP)是人工智能领域中一个充满活力和挑战的分支。要让计…...
手写tomcat:基本功能实现(4)
逻辑架构 HTTP 请求与 Socket: 左侧的 “HTTP 请求” 箭头指向 “socket”,表示客户端发送的 HTTP 请求通过 socket 传输到服务器。Socket 负责接收请求,并提取出其中的 请求路径(如 /first)和 请求方法(如…...
String的一些固定程序函数
append reverse length toString...
SECERN AI提出3D生成方法SVAD!单张图像合成超逼真3D Avatar!
SECERN AI提出的3D生成方法SVAD通过视频扩散生成合成训练数据,利用身份保留和图像恢复模块对其进行增强,并利用这些经过优化的数据来训练3DGS虚拟形象。SVAD在新的姿态和视角下保持身份一致性和精细细节方面优于现有最先进(SOTA)的…...
windows触摸板快捷指南
以下是结构化整理后的触控手势说明,采用清晰的层级划分和标准化表述: **触控手势操作规范****1. 单指操作****2. 双指操作****3. 三指操作****4. 四指操作** **优化说明:** 触控手势操作规范 1. 单指操作 手势功能描述等效操作单击滑动选择…...
Mipsel固件Fuzzing小记
Mipsel固件Fuzzing小记 0x01 准备 1.1 安装必要工具链 首先需要安装 MIPS 交叉编译工具链和相关依赖: sudo apt-get install -y gcc-mipsel-linux-gnu g-mipsel-linux-gnu binwalk qemu-user-static afl这些工具分别用于:交叉编译、固件解包、二进制…...
边缘计算:物联网的“加速器”与“守护者”
引言 随着物联网(IoT)的快速发展,越来越多的设备接入网络,产生了海量的数据。传统的云计算架构面临着延迟高、带宽不足、数据安全等问题。边缘计算作为一种新兴技术,正在成为解决这些问题的关键手段。本文将探讨边缘计…...
简单网络交换、路由-华三RRPP以太环网
1、RRPP简单介绍 RRPP用来组建环网的链路层协议,工作在二层,比STP收敛更快,同时与STP、Smart-link互斥。很多企业很少应用环网组网,但是小编所在工业生产制造企业在特定工艺的区域对环网应用颇多,RRPP小编还是推荐网工…...
Kotlin变量与数据类型详解
Kotlin 变量与基本数据类型详解 一、变量声明 1. val vs var val:不可变变量(只读),类似 Java 的 finalvar:可变变量 val name "Kotlin" // 类型推断为 String var age 25 // 类型推断为 I…...
【Redis】List 列表
文章目录 初识列表常用命令lpushlpushxlrangerpushrpushxlpop & rpoplindexlinsertllen阻塞操作 —— blpop & brpop 内部编码应用场景 初识列表 列表类型,用于存储多个字符串。在操作和实现上,类似 C 的双端队列,支持随机访问(O(N)…...
React中useState中更新是同步的还是异步的?
文章目录 前言一、useState 的基本用法二、useState 的更新机制1. 内部状态管理2. 状态初始化3. 状态更新 三、useState 的更新频率与异步行为1. 异步更新与批量更新2. 为什么需要异步更新? 四、如何正确处理 useState 的更新1. 使用回调函数形式的更新2. 理解异步更…...
Python语法强化
在正式编写第一个Python程序前,我们先复习一下什么是命令行模式和Python交互模式。 命令行模式 在Windows开始菜单选择“Terminal”,就进入到PowerShell命令行模式,它的提示符类似PS C:\>: ┌───────────────…...
FastMCP:为大语言模型构建强大的上下文和工具服务
FastMCP:为大语言模型构建强大的上下文和工具服务 在人工智能快速发展的今天,大语言模型(LLM)已经成为许多应用的核心。然而,如何让这些模型更好地与外部世界交互,获取实时信息,执行特定任务&am…...
TC3xx学习笔记-UCB BMHD使用详解(二)
文章目录 前言Confirmation的定义Dual UCB: Confirmation StatesDual UCB: Errored State or ECC Error in the UCB Confirmation CodesECC Error in the UCB ContentDual Password UCB ORIG and COPY Re-programming UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0-3)BMHD Protecti…...
【Docker】docker compose和docker swarm区别
Docker Swarm 和 Docker Compose 的核心区别: 1. 定位不同 Docker Compose 单机多容器编排:在单个主机上管理多个容器,适合本地开发、测试环境。单节点部署:所有容器运行在同一 Docker 引擎实例上。 Docker Swarm 集群管理工具&…...
Power BI Desktop开发——矩阵相关操作
本篇文章使用2025年5月17日从微软商店下载的最新版Power BI Desktop 目录 1.设置矩阵网格整体大小 2.设置矩阵网格行高 3.设置矩阵网格列宽 4.隐藏矩阵网格的某一列 5.隐藏矩阵网格的某一行 6.设置矩阵网格居中展示 7.号图表的显示设置 8.调整行标题的缩进 9.设置矩阵…...
系统架构设计(九):分布式架构与微服务
基础定义 架构类型定义分布式架构指将系统部署在多个服务器节点上,通过网络协作完成整体功能。强调物理上的分布与任务协作。微服务架构一种分布式架构模式,将系统按照业务维度拆分为多个小型自治服务,每个服务可独立开发、部署、伸缩。 核…...
Linux服务器安全如何加固?禁用不必要的服务与端口如何操作?
保护Linux服务器的安全性对于确保系统的稳定性和数据的保密性至关重要。加固Linux服务器的安全性包括禁用不必要的服务和端口,以减少潜在的攻击面。本文将探讨如何加固Linux服务器的安全性,具体介绍如何禁用不必要的服务和端口,从而提高服务器…...
AgentCPM-GUI,清华联合面壁智能开源的端侧GUI智能体模型
AgentCPM-GUI是什么 AgentCPM-GUI 是由清华大学与面壁智能团队联合开发的一款开源端侧图形用户界面(GUI)代理,专为中文应用进行优化。基于 MiniCPM-V 模型(80 亿参数),该系统能够接收智能手机的屏幕截图&a…...
如何用AI优化简历:自动读取与精华浓缩
在求职过程中,一份出色的简历往往是成功的关键。然而,许多求职者在撰写简历时往往面临诸多挑战,比如如何让简历更突出、如何让招聘者快速了解自己的核心优势等。随着人工智能技术的发展,AI不仅可以帮助我们优化简历内容࿰…...
Jackson使用详解
JSON Jackson是java提供处理json数据序列化和反序列的工具类,在使用Jackson处理json前,我们得先掌握json。 JSON数据类型 类型示例说明字符串(String)"hello"双引号包裹,支持转义字符(如 \n&a…...
Node.js 源码概览
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它的源码结构相当庞大且复杂。下面我将为你讲解 Node.js 源码的主要结构和关键组成部分。 源码结构 Node.js 的主要源码目录结构如下: node/ ├── lib/ # JavaScript 核心模…...
简单神经网络(ANN)实现:从零开始构建第一个模型
本文将手把手带你用 Python Numpy 实现一个最基础的人工神经网络(Artificial Neural Network, ANN)。不依赖任何深度学习框架,适合入门理解神经网络的本质。 一、项目目标 构建一个三层神经网络(输入层、隐藏层、输出层…...
Conda 完全指南:从环境管理到工具集成
Conda 完全指南:从环境管理到工具集成 在数据科学、机器学习和 Python 开发领域,环境管理一直是令人头疼的问题。不同项目依赖的库版本冲突、Python 解释器版本不兼容等问题频繁出现,而 Conda 的出现彻底解决了这些痛点。作为目前最流行的跨…...
防范Java应用中的恶意文件上传:确保服务器的安全性
防范Java应用中的恶意文件上传:确保服务器的安全性 在当今数字化时代,Java 应用无处不在,而文件上传功能作为许多应用的核心组件,却潜藏着巨大的安全隐患。恶意文件上传可能导致服务器被入侵、数据泄露甚至服务瘫痪,因…...
CSS- 4.2 相对定位(position: relative)
本系列可作为前端学习系列的笔记,代码的运行环境是在HBuilder中,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。 HTML系列文章 已经收录在前端专栏,有需要的宝宝们可以点击前端专栏查看! 点…...