Spring MVC

目录

一、了解Servlet

二、什么是Spring MVC?

2.1 MVC定义

2.2 Spring MVC介绍

三、学习Spring MVC

3.1 建立连接

3.1.1 @RequestMapping注释介绍

3.1.2 @RequestMapping使用

3.1.3 @RequestMapping是GET请求还是POST请求?

 3.2 请求

3.2.1 传递单个参数

3.2.2 传递多个参数 

3.2.3 传递对象

3.2.4 后端参数重命名 

3.2.5 传递数组

3.2.6 传递集合

3.2.7 传递JSON对象

3.2.8 获取URL中参数@PathVariable

3.2.9 上传文件 

3.2.10 获取Header 

3.2.10.1 传统获取header

3.2.10.2 简洁获取Header 

​3.3 响应 

3.3.1 返回静态页面

3.3.2 返回数据@ResponseBody

3.3.3 返回HTML代码片段

3.3.4 返回JSON

​编辑 3.3.5 设置状态码

​编辑 3.3.6 设置Content-Type

3.4 注解源码讲解 

3.4.1 @RestController源码

3.4.2 @Controller源码

 3.4.3 @ResponseBody源码


一、了解Servlet

        要想了解Spring MVC,先理解下Servlet,Servlet是一种实现动态页面的技术,准确来讲Servlet是一套Java Web开发的规范,或者说是一套Java Web开发的技术标准。 

二、什么是Spring MVC?

        Spring MVC全程Spring Web MVC,它是基于Servlet API构建的原始Web框架,从一开始就包含在Spring框架中。它正式名称为Spring Web MVC,但它通常被称为Spring MVC,Spring MVC是一个WEB框架。

2.1 MVC定义

        MVC是Model View Controller的缩写,它是软件工程中的一种软件架构设计模式,它把软件系统分为模式、视图和控制器三个基础部分。

  • View(视图):指在应用程序中专门用来与浏览器进行交互,展示数据的资源
  • Model(模型):是应用程序的主题部分,用来处理程序中数据逻辑的部分
  • Controller(控制器):可以理解为一个分发器,用来决定对于视图发来的请求,需要用哪一个模型来处理,以及处理完后跳回哪一个视图

举例:

比如去饭店吃饭,客户进店之后,服务员来接待客户点餐,客户点餐之后,把客户菜单交给前厅,前厅根据客户菜单给后厨下达命令,后厨负责做饭,做完之后,再根据菜单告诉服务员,这是哪位客人的饭菜。

在这个过程中:

服务员就是View(视图):负责接待客户,帮助客人点餐以及给顾客端饭

前厅就是Controller(控制器):根据用户的点餐情况,来选择给哪个后厨下达命令

后厨就是Model(模型):根据前厅的要求完成客户的用餐需求

2.2 Spring MVC介绍

        MVC是一种架构设计模式,也是一种思想,而Spring MVC是对MVC思想的具体实现,除此之外,Spring MVC还是一个Web框架。

总结来说,Spring Mvc是一个实现了MVC模式的Web框架。

        其实,Spring MVC我们在前面已经用过了,在创建Spring Boot项目时,我们在勾选Spring Web框架其实就是Spring MVC框架: 

这可能就有点懵了,前面创建的不是Spring Boot项目嘛?怎么又变成了Spring MVC项目了?它们之间到底有着什么样的联系? 

Spring Boot只是实现Spring MVC的其中一种方式而已,Spring Boot可以添加很多依赖,借助这些依赖实现不同的功能,Spring Boot通过添加Spring Web MVC框架来实现web功能。

不过在Spring在实现MVC时,也结合自身项目的特点,做出了一些改变,相对而言,下面的图或许更适合一些。 

比如上面的例子,去饭店吃饭,一些饭店是前厅来负责接待客户,帮助客户点餐,也就是Controller来负责接收用户的请求

再举一个例子: 

比如你去面试公司,可能直接是面试团队来接待候选人,直接省去了HR中间的交接过程,再由面试团队挑选面试官对你进行面试,面试官再将面试情况返回给面试团队,再转交给HR,HR再通知你是否面试通过。

三、学习Spring MVC

学习Spring MVC,重点也就是学习如何通过浏览器和用户程序进行交互

主要分以下三个方面:

  • 建立连接:将用户(浏览器)和Java程序连接起来,也就是访问一个地址能够调用到我们的Spring程序。
  • 请求:用户请求的时候会带一些参数,在程序中要想办法获取到参数,所以请求这块主要是获取参数的功能。
  • 响应:执行了业务逻辑之后,要把程序执行的结果返回给用户,也就是响应。

3.1 建立连接

在Spring MVC中使用@RequestMapping来实现URL路由映射,也就是浏览器连接程序的作用。

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

@RestController
public class UserController {
    @RequestMapping("/sayHi")
    public String sayHi() {
        return "hello, Spring MVC";
    }
}

接下来访问:http://127.0.0.1:8080/sayHi,就可以看到程序返回的数据了

3.1.1 @RequestMapping注释介绍

@RequestMapping是Spring Web MVC应用程序中最常用的注解之一,它是用来注册接口的路由映射的。

表示服务收到请求时,路径为/sayHi的请求就会调用sayHi这个方法的代码。

路由映射:当用户访问一个URL时,将用户的请求对应到程序中某个类的某个方法的过程就叫路由映射。

既然@RequestMapping已经达到了我们的目的,为什么还需要加@RestController注解呢?

我把@RestController注解去掉,再访问一次:

可以看到,程序报了404,找不到页面

这就是@RestController起到的作用

是因为一个项目中会有很多类,每个类可能会有很多的方法,Spring程序怎么知道要执行哪个方法呢?

Spring会对所有的类进行扫描,如果类加了注解@RestController,Spring才会去看这个类里面的方法有没有加@RequestMapping这个注解,当然它的作用不止这一点,后续会详细介绍。

3.1.2 @RequestMapping使用

@RequestMapping既可修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类路径+方法路径。

  • @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
  • @RequestMapping标识一个方法:设置映射请求请求路径的具体信息

为什么 @RequestMapping既可修饰类,也可以修饰方法?

点进去@RequestMapping源码进行查看

红色框中是它的元注解,表示这个注解既可修饰方法 ,也可修饰方法,具体后面介绍。

注意

  • 我们在写项目时,最好也用@RequestMapping注解修饰类,因为我们要是没加这个类注解的话,Spring在进行扫描的时候,不同的类中可能存在相同的修饰方法的@RequestMapping注解,这就会导致找不到究竟执行哪条命令。
  • @RequestMapping的URL路径最前面加不加/都可以,Spirng程序启动时,会进行判断,如果前面没有加/,Spring会接上一个/

3.1.3 @RequestMapping是GET请求还是POST请求?

此处我们使用Postman来发送请求

1、发送GET请求

2、发送POST请求

通过上图发现,@RequestMapping既支持GET请求也支持POST请求。

 3.2 请求

        访问不同的路径,就是发送不同的请求,在发送请求时,可能会带一些参数,所以学习Spring的请求,主要学习如何传递参数到后端以及后端如何接收。

传递参数,此处使用Postman来模拟。

3.2.1 传递单个参数

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

@RestController
@RequestMapping("/param")
public class Param {
    @RequestMapping("/m1")
    public String param(String name) {
        return "接到的参数name:" + name;
    }
}

3.2.2 传递多个参数 

@RequestMapping("/m2")
    public String param2(String name, String password) {
        return "接到的参数name:" + name + ", password:" + password;
    }

注意:当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响后端参数的结果。

3.2.3 传递对象

如果参数比较多,方法声明就需要有很多形参,并且后续每次新增一个参数,也需要修改方法声明,我们不妨把这些参数封装成一个对象。

@RequestMapping("/m3")
    public String param3(Person p) {
        return p.toString();
    }

3.2.4 后端参数重命名 

某些特殊的情况下,前端传递的参数 key 和我们后端接收的key不⼀致,这样就会出现参数接收不到的情况,如果出现这种情况,就可以使用 @RequestParam 来重命名前后端的参数值。
@RequestMapping("/m4")
    public String param4(@RequestParam("time") String createTime) {
        return "接收到的参数createTime:" + createTime;
    }
       

注意: 

  • 使用@RequestParam进行参数重命名时,请求参数只能和@RequestParam声明的名称一致,才能进行参数绑定和赋值。
  • 使用@RequestParam进行参数重命名时,参数就变成了必传参数。

如果想注解的参数不是必传参数呢?

非必传参数设置

先来了解下参数必传的原因,我们查看@RequestParam注解的实现细节就可以发现端倪,注解实现如下:

可以看到required的默认值为true,表示的含义就是:该注解修饰的参数为必传 。

既然如此,我们可以用过设置@RequestParam中的required=false来避免不传递时报错,具体实现如下:

@RequestMapping("/m4")
    public String param4(@RequestParam(value = "time", required = false) String createTime) {
        return "接收到的参数createTime:" + createTime;
    }

可以看到添加required=false后,在请求的URL中没有添加time参数,程序没有报错。

3.2.5 传递数组

@RequestMapping("/m5")
    public String param5(String[] arrayParam) {
        return Arrays.toString(arrayParam);
    }

 3.2.6 传递集合

集合参数:和数组类似,同一个请求参数名有很多个,且需要使用@RequestParam绑定参数关系

@RequestMapping("/m6")
    public String param6(@RequestParam List<String> list) {
        return "size:" + list.size() + ",listParam:" + list;
    }

3.2.7 传递JSON对象

接收JSON对象,需要使用@RequestBody注解

@RequestBody:请求正文,意思就是这个注解作用在请求正文的数据绑定,请求参数必须在写请求正文中。

@RequestMapping("/m7")
    public String param7(@RequestBody Person person) {
        return person.toString();
    }

通过Fiddler观察一下请求参数:

 3.2.8 获取URL中参数@PathVariable

path variable:路径变量

和字面表达的意思一样,这个注解主要作用在请求URL路径上的数据绑定

@RequestMapping("/m8/{id}/{name}")
    public String param8(@PathVariable Integer id, @PathVariable("name") String username) {
        return "解析参数id:" + id + ",name:" + username;
    }

3.2.9 上传文件 

@RequestMapping("/m9")
    public String param9(@RequestPart("file") MultipartFile file) throws IOException {
        //获取文件名称
        String filename = file.getOriginalFilename();
        //文件上传到指定路径
        file.transferTo(new File("G:/temp" + filename));
        return "文件名称:" + filename;
    }

 

3.2.10 获取Header 

3.2.10.1 传统获取header

获取Header也是从HttpServletRequest中获取

@RequestMapping("/getHeader")
    public String getHeader(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        return "userAgent:" + userAgent;
    }

通过Fiddler观察,获取的User-Agent是否正确

3.2.10.2 简洁获取Header 
@RequestMapping("/getHeader2")
    public String getHeader2(@RequestHeader("User-Agent") String userAgent) {
        return "userAgent: " + userAgent;
    }

3.3 响应 

在我们前面的代码例子中,都已经设置了响应数据,HTTP响应的结果可以是数据,也可以是静态页面,也可以针对响应设置状态码,Header信息等。

3.3.1 返回静态页面

后台代码:

@RequestMapping("/return")
@RestController
public class IndexController {
    @RequestMapping("/index")
    public String returnIndex() {
        return "/index.html";
    }
}

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index页面</title>
</head>
<body>
    hello,Spring MVC
</body>
</html>

运行结果;

 

结果却发现,页面未正确返回,http响应把"/index.html"当做了http响应正文的数据,那Spring MVC如何才能识别出来index.html是一个静态页面,并进行返回呢?

我们需要把@RestController改为@Controller

代码如下:

@RequestMapping("/return")
@Controller
public class IndexController {
    @RequestMapping("/index")
    public String returnIndex() {
        return "/index.html";
    }
}

发现页面正确展示了 

@RestController和@Controller有着什么样的关联和区别呢?

咱们前面讲了MVC模式,后端会返回视图,这是早期的概念,随着互联网的发展,目前项目开发流行前后端分离模式,MVC的概念也逐渐发生了变化,View不再返回视图,而是返回显示视图所需要的数据。

所以前面使用的@RestController其实是返回的数据

@RestController = @Controller + @ResponseBody

3.3.2 返回数据@ResponseBody

@RequestMapping("/return")
@Controller
public class IndexController {

    @RequestMapping("/returnHtml")
    @ResponseBody
    public String returnData() {
        return "/index.html";
    }
}

加上@ResponseBody注解,该方法就会把"/index.html"当做一个数据返回给 

@ResponseBody既是类注解,又是方法注解

如果作用在类上,表示该类的所有方法,返回的都是数据,如果作用在方法上,表示该方法返回的是数据。

也就是说:在类上添加@ResponseBody就相当于在所有的方法上添加了@ResponseBody注解,

同样,如果类上有@RestController注解时,表示所有的方法上添加了@ResponseBody注解,也就是当前类下的所有方法返回值为响应数据。

注意:如果一个类的方法里,既有返回数据的,又有返回页面的,就把@ResponseBody注解添加到对应的方法上即可。

3.3.3 返回HTML代码片段

@RequestMapping("/return")
@Controller
public class IndexController {

    @RequestMapping("/returnHtml2")
    @ResponseBody
    public String returnHtml() {
        return "<h1>返回Html代码片段</h1>";
    }
}

 

后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析 

3.3.4 返回JSON

后端方法返回结果为对象

@RequestMapping("/return")
@Controller
public class IndexController {
    
    @RequestMapping("/returnJson")
    @ResponseBody
    public HashMap<String, String> returnJson() {
        HashMap<String, String> map = new HashMap<>();
        map.put("java", "java value");
        map.put("MySQL", "MySql value");
        return map;
    }
}

 

通过Fidder观察响应结果,Content-Type为application/json

 3.3.5 设置状态码

Spring MVC会根据我们方法的返回结果自动设置响应状态码,程序员也可以手动指定状态码

@RequestMapping("/return")
@Controller
public class IndexController {

    @RequestMapping("/setStatus")
    @ResponseBody
    public String setStatus(HttpServletResponse response) {
        response.setStatus(401);
        return "设置状态码";
    }
}

 

通过Fidder观察响应结果:

 3.3.6 设置Content-Type

通过设置produces属性的值,设置响应的报头Content-Type

@RequestMapping("/return")
@Controller
public class IndexController {

    @RequestMapping(value = "r1", produces = "application/json; charset=utf8")
    @ResponseBody
    public String r1(HttpServletResponse response) {
        //设置header
        response.setHeader("head", "myhead");
        return "{'OK': 1}";
    }
}

 

通过Fidder观察响应结果:

3.4 注解源码讲解 

3.4.1 @RestController源码

 点进去RetentionPolicy,截图如下:

 

Java注解的生命周期:

  • SOURCE:注解只保留在源文件,当Java源文件编译成class文件的时候,注解被遗弃
  • ClASS:注解被保留到class文件中,但JVM加载class文件时候被遗弃
  • RUNTIME:注解不仅保存到class文件中,JVM加载class文件之后仍然存在

这3个生命周期分别对应于:Java源文件(.java文件)—>.class文件—>内存中的字节码

3.4.2 @Controller源码

 3.4.3 @ResponseBody源码

 

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值