首先,关于 SspringMVC 要知道
- spring MVC是一个web框架
- spring MVC是基于servlet API构建的
1. 关于MVC
MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
- Model(模型)是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图)是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的。
- Controller(控制器)是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据,控制⽤户输⼊,并向模型发送数据。
1.2 MVC 和 Spring MVC 的关系
MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
(相当于spring中的IOC与DI,IOC是(控制反转)思想,DI(依赖注入)是具体实现)
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求。
2. @RequestMapping 注解
@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的。
路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类的某个⽅法的过程就叫路由映射。
@RequestMapping 基础使⽤:
@Controller //注册类到spring容器中
@RequestMapping("/student")//一级路由映射
@ResponseBody //程序不报错,返回一个非静态页面数据
public class StudentController {
@RequestMapping("/sayhi")
public String sayHi(){
return "hello!";
}
}
@RequestMapping 即可修饰类,也可以修饰⽅法,当修饰类和⽅法时,访问的地址是类 + ⽅法。
@RequestMapping 也可以直接修饰⽅法,代码实现如下:
@Controller //注册类到spring容器中
@ResponseBody //程序不报错,返回一个非静态页面数据
public class StudentController {
@RequestMapping("/sayhi")
public String sayHi(){
return "hello!";
}
}
注意:
- @RequestMapping 即可处理get请求,也可处理post请求
2.1 @GetMapping 和 @PostMapping
get 请求的 3 种写法:
// 写法1
@RequestMapping("/index")
// 写法2
@RequestMapping(value = "/index",method = RequestMethod.GET)
// 写法3
@GetMapping("/index")
post 请求的 2 种写法:
// 写法1
@RequestMapping(value = "/index",method = RequestMethod.POST)
// 写法2
@PostMapping("/index")
3. 获取参数系列注解
3.1 传递单个参数
在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,⽐如以下代码:
@Controller //注册类到spring容器中
@ResponseBody //程序不报错,返回一个非静态页面数据
public class StudentController {
@RequestMapping("/m1")
public String getPara(String name){
return "接收到的name是 :"+name;
}
}
3.2 传递对象
Spring MVC 可以⾃动实现参数对象的赋值,⽐如 Person 对象:
@Data //自动导入get/set/toString方法
public class Person {
private int id;
private String name;
private String password;
}
传递对象代码实现:
@RequestMapping("/m2")
public String getPara2(Person person){
return person.getId()+person.getName()+person.getPassword();
}
访问对应路由:
3.3 表单参数传递/传递多个参数(非对象)
@RequestMapping("/m3") //传递多个参数,方法形参里面相应加,写就行了
public String getParas(String name,String pwd){
return name+" "+pwd;
}
重要说明:当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置是不影响后端获取参数的结果。
3.4 后端参数重命名(后端参数映射)
某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个name 给后端,⽽后端⼜是有 username 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值.
@RequestMapping("/m4")
public String getPara4(@RequestParam("name") String username){
return username;
}
这个代码就实现了前端传递参数的名称和后端代码规定的参数名称不一致的问题,使用 @RequestParam 注解就实现了前端虽然传递的是name,但是后端username也能够正确接收该参数,避免了修改参数名称的麻烦。
上⾯的列⼦,如果我们是前端传递⼀个⾮ name 的参数,就会出现程序报错的情况,如下图所示:
这是因为后端已经声明了前端必须传递⼀个 name 的参数,但是前端没有给后端传递,我们查看@RequestParam 注解的实现细节就可以发现端倪,注解实现如下:
required:必须的意思。默认值是TRUE,因此不传递此参数就会报400的错误
如果我们的实际业务前端的参数是⼀个⾮必传的参数,我们可以通过设置 @RequestParam 中的required=false 来避免不传递时报错,具体实现如下:
@RequestMapping("/m4")
public String getPara4(@RequestParam(value = "name",required = false) String username){
return username;
}
设置required为FALSE时候,此时如果前端传递的参数不是name时候,程序也不会报错停止了。
3.5 @RequestBody 接收JSON对象
后端接收代码为:
@RequestMapping(value = "/m5",method = RequestMethod.POST)
public String getJson(@RequestBody Person person){
return person.toString();
}
使用postman模拟发送json数据的post请求:
响应如下:
成功获取json格式的数据并传参给了person对象~
此时如果去除掉 @RequestBody 注解:
再发送请求尝试获取就发现获取不到了,赋的值都是默认值:
3.6 获取URL中参数@PathVariable
后端实现代码:
@RequestMapping("/m6/{name}/{password}")
public String getURL(@PathVariable String name,@PathVariable String password){
return "name: "+name+" "+"password: "+password;
}
发送请求:
获取成功
注意:
注意事项:@RequestMapping(“/m6/{name}/{password}”)中{password} 参数不能省略,要是省略就会报错:
3.7 上传文件@RequestPart
模拟实现根据用户id实现上传头像到服务器端的代码(修改头像需求):
@RequestMapping("/m7")
public boolean upImg(Integer id, @RequestPart("img")MultipartFile file){
boolean result=false;
String fileName=file.getOriginalFilename();//得到图片的名称 xxx.xxx
fileName=fileName.substring(fileName.lastIndexOf("."));//得到图片后缀
fileName= UUID.randomUUID().toString()+fileName;//构建出图片全球唯一id
try {
file.transferTo(new File("D:\\"+fileName));
result=true;
} catch (IOException e) {
log.error("上传图片失败:"+e.getMessage());
}
return result;
}
postman模拟传文件:
此时相应目录中就生成了带有UUID(全球唯一标识符)的图片文件:
3.8 获取Cookie/Session/header
servlet 传统获取 header/cookie:
@RequestMapping("/m8")
public String getCH(HttpServletRequest request){
Cookie[] cookies= request.getCookies(); //拿到所有cookie
String userAgent= request.getHeader("User-Agent");
return userAgent;
}
简洁的获取 Cookie—@CookieValue:
@RequestMapping("/m9")
@ResponseBody
public String getCookie(@CookieValue("xxx") String value){
return "cookie:"+value; //根据cookie的key(xxx),获取相应的value
}
简洁获取 Header—@RequestHeader:
@RequestMapping("/m10")
@ResponseBody
public String getHeader(@RequestHeader("User-Agent") String ua){
return "user-agent:"+ua;
}
获取成功 ~
Session 存储和获取 :
Session 存储和 Servlet 类似,是使⽤ HttpServletRequest 中获取的,如下代码所示:
@RequestMapping("/m11")
public String setSess(HttpServletRequest request){
//获取Httpsession对象,参数设置为TRUE,表示如果没有session对象就创建一个session
//设置为FALSE表示如果没有就不创建session
//一般只有在存的时候设置TRUE,其余情况一般都为FALSE
HttpSession session=request.getSession(true);
session.setAttribute("username","zhangsan");
return "session 存储成功";
}
读取 Session 可以使⽤ HttpServletRequest,如下代码所示:
@RequestMapping("/m12")
public String getSess(HttpServletRequest request){
//如果session不存在不会自动创建
HttpSession session=request.getSession(false);
String username="null";
if (session!=null && session.getAttribute("username")!=null){
username= (String) session.getAttribute("username");
}
return "username: "+username;
}
session存储成功之后,且在没有重启服务器的情况下尝试获取session:
session获取成功 ~
获取 Session 更简洁的⽅式:
@RequestMapping("/m13")
public String getsess2(@SessionAttribute(value = "username",required = false)String username){
return "username: "+username;
}
设置required = false (默认是true),表示即使没有获取到对应session时候程序也不会报错;
session获取成功 ~
4. 返回数据注解总结
默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图(xxx.html),⽽现在都是前后端分离的,后端只需要返给给前端数据即可,这个时候我们就需要使⽤@ResponseBody 注解了。
也就是说 @ResponseBody 注解的作用就是允许后端返回一个非静态页面的数据给前端;
- @ResponseBody 返回的值如果是字符会转换成 text/html,如果返回的是对象会转换成
application/json 返回给前端。 - @ResponseBody 可以⽤来修饰⽅法或者是修饰类,修饰类表示类中的所有⽅法都会返回 html 或者json,⽽不是视图。
4.1 返回 JSON 对象
后端实现代码:
@RequestMapping("/m14")
public HashMap<String,String> returnJson(){
HashMap<String,String> map=new HashMap<>();
map.put("Java","Java value");
map.put("Mysql","mysql value");
return map;
}
前端发送请求:
抓包结果如下:
返回json格式数据成功~
4.1.1 登录功能实现(前端使用 ajax,后端返回 json 给前端)
这里实现为了简化代码,逻辑聚焦在发送和获取数据,就默认用户名和密码都是admin,不调用数据库啦;
前端页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/jQuery.js"></script>
<title>Document</title>
<script>
//ajax提交
function mysub(){
//1.判空
var username=jQuery("#username");
var password=jQuery("#password");
if(jQuery.trim(username.val())==""){
alert("请先输入用户名!");
username.focus();
return;
}
if(jQuery.trim(password.val())==""){
alert("请先输入密码!");
password.focus();
return;
}
jQuery.ajax({
url:"/login2",
type:"POST",
contentType:"application/json",
data:JSON.stringify({"username":username.val(),"password":password.val()}),
success:function(result){
alert(JSON.stringify(result));
}
});
}
</script>
</head>
<body>
<div style="text-align: center;">
<h1>登录</h1>
用户:<input id="username">
<br>
密码:<input id="password" type="password">
<br>
<input type="button" value="提交" onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
</div>
</body>
</html>
后端代码1:
使用userInfo对象和 @RequestBody 获取前端发送的json数据并自动给对象相应字段set值:
@RequestMapping("/login")
public HashMap<String,Object> login(@RequestBody UserInfo userInfo){
HashMap<String, Object> result = new HashMap<String, Object>();
int state = 200;
int data = -1; // 等于 1,登录成功,否则登录失败
String msg = "未知错误";
if (StringUtils.hasLength(userInfo.getUsername()) && StringUtils.hasLength(userInfo.getPassword())) {
if (userInfo.getUsername().equals("admin") && userInfo.getPassword().equals("admin")) {
data = 1;
msg = "";
} else {
msg = "用户名或密码失败!";
}
} else { // 参数为空
msg = "非法参数";
}
result.put("state", state);
result.put("data", data);
result.put("msg", msg);
return result;
}
后端代码2:
使用对应形参获取前端发送的json数据:
@RequestMapping("/login2")
public HashMap<String,Object> login2(String username,String password){
HashMap<String, Object> result = new HashMap<String, Object>();
int state = 200;
int data = -1; // 等于 1,登录成功,否则登录失败
String msg = "未知错误";
if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
if (username.equals("admin") && password.equals("admin")) {
data = 1;
msg = "";
} else {
msg = "用户名或密码失败!";
}
} else { // 参数为空
msg = "非法参数";
}
result.put("state", state);
result.put("data", data);
result.put("msg", msg);
return result;
}
实验:
登录功能成功实现!重点明白如何使用ajax发送json和后端接送json数据的方式!
5. 请求转发或请求重定向
5.1 请求转发(forward)
实现方式1:
//请求转发
@RequestMapping("/fw1")
public String myForward1(){
return "forward:/hello.html";
}
实现方式2:
@RequestMapping("/fw2")
public String myForward2(){
return "/hello.html";
}
实现方式3:
@RequestMapping("/fw3")
public void myForward3(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/hello.html").forward(request,response);
}
三种方式都能实现 请求转发(forward):
5.2 请求重定向(redirect)
实现方式1:
@RequestMapping("/rd1")
public String myRedirect(){
return "redirect:/hello.html";
}
实现方式2:
@RequestMapping("/rd2")
public void myRedirect2(HttpServletResponse response) throws IOException {
response.sendRedirect("/hello.html");
}
均能实现页面跳转:
5.3 forward 和 redirect 区别
使用fiddler抓包可以发现:
请求转发(forward)只有一个包:
请求重定向(redirect)有两个包(发送了两次请求):
综上可以得出:
- 请求转发(forward)是服务器端行为
- 请求重定向(forward)是客户端(浏览器)行为
- 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发
- 请求重定向地址发⽣变化,请求转发地址不发生变化
- 请求重定向与直接访问新地址效果一致,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问
- (举列说明:如果要转发的地址hello.html和资源文件js\hello.js不在同一级目录下,使用请求转发就会导致hello.html的资源文件hello.js获取不到;而使用请求重定向就不会存在这个问题,能够正常获取hello.js这个资源文件 ~~)
6. 查看更多的springMVC注解
- 完结撒花~
- over!