一篇文章教你如何快速上手Spring MVC框架【万字详解|包含常用注解分析讲解】

目录

一.什么是Spring Web MVC

二.Spring MVC的使用

▐ 建立连接

@RestController

@RequestMapping

▐ 传递参数

1.简单类型传参

2.类对象传参(@RequestParam)

3.数组&集合传参

4.JSON传参(@RequestBody)

5.URL中的参数(@PathVariable)

6.上传文件(@RequestPart)

7.Cookie传参(@CookieValue)

8.Session传参(@SessionAttribute)

 9.获取Header(@RequestHeader)

▐ 响应 

@Controller & @ResponseBody


一.什么是Spring Web MVC

首先引入官方的一段文字:

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC," comes from 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)的名称,但更常见的名称是“Spring MVC”。 

我们知道 Servlet 是⼀套Java Web 开发的规范,是⼀种实现动态⻚⾯的技术。也就是说,Spring Web MVC 是为了简化原有 Java Web 中的原生 Servlet 操作而产生的一套框架,并且他还有一个更常见的名称,也就是Spring MVC 

要理解什么是Spring Web MVC 之前,我们需要先理解什么是MVC

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

  • View(视图) 指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源。
  • Model(模型) 是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分。
  • Controller(控制器)可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型。

这种软件架构模式大大优化了软件开发效率。

而我们知道的,Spring最擅长的事情就是整合,吸收别人的好的部分转为自己的一部分。对于这种优秀的架构模型,Spring对齐也进行了整合,对于整合后的结果也有一个响亮的名字,也就是我们这篇文章的主角——Spring MVC

换言之,MVC是一种架构设计模式,而Spring MVC是对MVC思想的具体体现。

  • MVC:⼀种软件架构设计模式
  • Spring MVC:一个Web开发框架,吸收了MVC的这种设计模式。

我们通过下图可以更好的理解到Spring MVC与MVC之间的对照关系。


二.Spring MVC的使用

其实在我们创建一个新的SpringBoot项目的时候,在我们选择依赖的时候就可以看见一个Spring Web的依赖,这个其实就是我们实际上使用的Spring MVC。如下图我们可以看见官方的解释:

Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.

翻译为中文就是:使用Spring MVC构建web(包括RESTful)应用程序。使用Apache Tomcat作为默认的嵌入式容器。

我们说到Spring MVC是一个web开发框架,那既然是一个Web开发框架,它对于一个Web应用的关注点是什么呢?我们可以大致将Spring MVC要做的三件事总结如下:

  1. 建立连接:将⽤⼾(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的Spring 程序。
  2. 传递参数:⽤⼾请求的时候会带⼀些参数,在程序中要想办法获取到参数,然后进行业务逻辑操作。
  3. 返回结果:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤⼾。

通过以上三部分,用户就可以在浏览器输入URL之后得到用户想要的界面和数据了。对于 Spring MVC 来说,掌握了以上 3 个功能也就相当于掌握了 Spring MVC。

▐ 建立连接

就笔者的经验之谈,学习Spring MVC其实就是学习了其中的注解,Spring程序往往都是通过各种各样的注解来实现各自的功能的。对于建立连接这样的一个过程中,有俩个注解格外的重要:

  • @RestController
  • @RequestMapping

@RestController

我们知道MVCC代表着就是Controller,它主要的用途就是用来接收用户的请求,我们可以打开 @RestController 的源码:

我们可以看到有一个 @Target({ElementType.TYPE}) 的注解,这个注解的就是表明了 @RestController 的作用范围,后面括号内跟着的参数 ElementType.TYPE 就表明了这个注解是作用于 “” 上的。那么这个类就相对于MVC架构中的Controller,这个类就可以用来接收用户请求,如下是一个简单的示例:

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

/**
 * @Author Luming
 * @Date 2024/7/9 20:31
 * @Description:
 */
@RestController
public class UserController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello spring mvc";
    }
}

有了能接收用户请求的功能还是不够的,用户请求的数据可能是多种多样的,我们如何对于这些不同的请求做出相对应的业务逻辑和响应呢?这就需要另一个注解 @RequestMapping

@RequestMapping

从字面理解:Request表示请求,Mapping表示映射关系,那么@RequestMapping 就是对于用户不同的请求做出相对应的映射操作,即路由映射。

也就是说这个注解给用户提供了一个路径,只要用户访问了@RequestMapping 中的路径,那这个方法就会被执行,用户就可以得到返回值。

这样说可能会有一点抽象,我们结合实例来看看,还是用刚才的那个UserController来演示,我们在@RequestMapping 中用 “/hello” 表示了用户请求的路径,那当我们用浏览器来访问这个路径的时候,我们就可以拿到这个方法的返回值,如图:

(这里是localhost:8080是因为Tomcat服务器的默认端口就是8080,这是可以更改的)

另外,对于这里还有一些细节部分需要注意,我们打开@RequestMapping的源码看看:

这里同样有一个 @Target的注解,它内部参数是一个数组,数组中有俩个值,分别是ElementType.TYPE ElementType.METHOD,这也就表明了@RequestMapping这个注解既可以作用于方法之上,还可以作用于类之上。当修饰类和⽅法时,访问的地址是类路径 + ⽅法路径。

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

第二个 @Retention 表示注解的生命周期,RUNTIME就表示了他的生命周期是“运行时”。前三个注解常被称为元注解。@AlliasFor可以理解为是起一个别名,对应到图中,"path" 和 "value" 就相对于是同一个内容只不过是名称不一样。

  • value: 指定映射的URL
  • method: 指定请求的method类型, 如GET, POST等
  • consumes: 指定处理请求(request)的提交内容类型(Content-Type),例如application/json,text/html
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
  • Params: 指定request中必须包含某些参数值时,才让该⽅法处理
  • headers: 指定request中必须包含某些指定的header值,才能让该⽅法处理请求

我们还是用一个实例来说明,对于刚才的 UserController 我们在这个类之前又加入了一个@RequestMapping注解,该注解的值为 “/user”。

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

/**
 * @Author Luming
 * @Date 2024/7/9 20:31
 * @Description:
 */
@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello spring mvc";
    }
}

我们还是像之前那样打开连接,这一次我们会发现报错了,状态码404。

状态码404,从经验上判断基本上是请求路径有问题了。

那为什么会出现请求路径有误呢?这是因为我们一共有俩个@RequestMapping注解,而这俩个注解是存在优先级的,即类上的@RequestMapping注解高于方法上的,因此,我们需要重新调整一下请求路径,将方法的请求路径拼接在类的请求路径之后。这样我们就可以正常得到结果了。

另外,对于注解中路径前的斜杠是可以省略的(但不建议)

@RequestMapping("user")
@RestController
public class UserController {
    @RequestMapping("hello")
    public String hello() {
        return "hello spring mvc";
    }
}

为了规范,建议大家还是不要省略。

在刚才源码中,我们可以注意到@RequestMapping中还有其他属性

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping {
    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

其实我们往往是默认省略掉一些信息的,不管是前文中提到的 “/user” 还是 “/hello”,他们都是 value 这个属性的值,只不过在我们值填入一个参数的时候,它默认帮我们匹配上了。对于前文中的例子,其实它的完整写法是这样的:

@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return "hello spring mvc";
    }
}

我们通过 method 确定了该请求必须是GET请求,否则不进行响应。

@RequestMapping 中有很多属性:value表示请求的路径,method表示请求的方式(HTTP报文的请求方式),params表示请求的参数信息... ...

我们可以注意到源码中,对于 method 的数据类型是一个数组类型

我们打开对应的源码

可以发现这些就是HTTP请求方式的枚举类型数组,因此如果我们想要让一个请求拥有多种请求方式的话,就可以在请求数组中加入其他的请求方式,如下:

@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping(value = "/hello", method = {RequestMethod.GET, RequestMethod.POST})
    public String hello() {
        return "hello spring mvc";
    }
}

我们也可以用PostMan来验证一下:

我们可以看见GET和POST都是可以请求成功拿到返回值的,但是使用其他的请求方式,如PUT的时候就报错了

那有没有方法可以简化这样的操作呢?

答案是有的,我们可以使用 @GetMapping、@PostMapping 这样的注解直接代替原有的 @RequesMapping ,这样的操作见名知意,可以简化之前的操作。

//    @RequestMapping(value = "/hello", method = {RequestMethod.GET, RequestMethod.POST})
    @GetMapping("/hello")
    @PostMapping("/hello")
    @PutMapping("/hello")
  • @GetMapping代表GET请求方式
  • @PostMapping代表POST请求方式
  • ... ...

▐ 传递参数

解决了接收用户请求的问题后又一个问题接踵而至,如何接收用户请求中传递的参数?

1.简单类型传参

在传递参数部分其实和传统的 Servlet 是一样的,我们传过去的是HTTP请求报文,从请求体或者请求头中就可以获得携带的参数,只要传递的参数的 name 和接收的 name 一样就可以拿到数据。

就比如这里,我们用 String 类型的 username 接收

@RequestMapping("/request")
@RestController
public class RequestController {
    @RequestMapping("/param")
    public String param(String username) {
        return "用户名是:" + username;
    }
}

我们使用PostMan进行测试,只要传递的参数名和接收的参数名是一样的,那就可以自动获取到这个数据

在这里对于接收参数部分,我们往往建议使用包装类进行接收,这样可以避免很多问题,示例如下,这俩部分主要区别在于一个是使用 Integer 的包装类进行接收,一个是使用 Int 类进行接收

    @RequestMapping("/age1")
    public String age1(Integer age) {
        return "年龄为:" + age;
    }
    
    @RequestMapping("/age2")
    public String age2(int age) {
        return "年龄为:" + age;
    }

这俩者在正常传参的时候,其实效果是一样的

我们可以看见都接收到了年龄为19这样的参数,但是假如俩者都不传参呢?

对于使用包装类型 Integer 的age1方法,它只是没有收到参数,所以数据值为null

但是对于使用基础类型 Int 的age2方法,则直接报错了

在后端程序中也报出了相同的错误

因此,建议在使用的时候选择包装类型进行传参。

在传递多个参数时,参数直接相互顺序是不影响的,主要传递的参数名和接收的参数名能对的上就可以:

    @RequestMapping("param2")
    public String nameAndAge(String name, Integer age) {
        return name + "的年龄为:" + age;
    }

 

2.类对象传参(@RequestParam)

我们也可以将一个类对象作为参数来接收,在对象的类中只需要有对于每个字段的getset方法即可

public class Person {
    private String name;
    private Integer age;
    private String passwd;
    
    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 String getPasswd() {
        return passwd;
    }
    
    public void setPasswd(String passward) {
        this.passwd = passward;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", passward='" + passwd + '\'' +
                '}';
    }
}
    @RequestMapping("/person")
    public String person(Person person) {
        return person.toString();
    }

其实使用类来接收参数也只是参数更多一点的情况而已,所以大体情况还是一样的

在上述使用的过程中我们需要时刻注意接收的参数名必须和传递的参数名一致,有没有一种办法可以规避这样的问题,使得程序员可以自定义接收的参数名称呢?

答案是有的,即使用@RequestParam

我们知道对于传递的参数,大多都是key - value形式的键值对,某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个 name 给后端,⽽后端是使⽤ username 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使⽤ @RequestParam 来绑定前后端传参的关系,使得对于传参名称不一样的问题得以解决,就好像是对参数做了重命名一样。

就比如下面这段代码,我们后端使用的是 username 用来接收参数,为了接收前端传来的 name 参数就可以这样编码:

    @RequestMapping("/name")
    public String username(@RequestParam("name")String username) {
        return "用户名是:" + username;
    }

我们打开@RequestParam 的源码也可以看见这个注解是作用于参数的

在上图的源码中有这么一行代码

    boolean required() default true;

这就意味着我们默认一旦使用了这个注解进行参数的重命名,那么这个参数就必须要传过来,是必不可少的了,如果空传就会报错。

同样,既然是默认的,那么就可以更改,在使用的时候将该属性置为 false 即可。和我们之前讲的@RequestMapping 一样,一旦参数超过一个就得写出每个参数具体的内容了,这里的 value 代表需要接收的参数名

    @RequestMapping("/name")
    public String username(@RequestParam(value = "name", required = false)String username) {
        return "用户名是:" + username;
    }

3.数组&集合传参

首先是使用数组进行传参,我们可以发现只要参数名对的上,都是可以接收的 

    @RequestMapping("/arrParam")
    public String arrParam(String[] arrParam) {
        return "数组元素为:" + Arrays.toString(arrParam);
    }

那使用集合呢?

    @RequestMapping("/listParam")
    public String listParam(ArrayList<String> listParam) {
        return "集合元素为:" + listParam;
    }

我们会发现并没有收到这个参数,返回的值为空,这是因为当Spring接收到这个参数会自动将其绑定认为是一个数组,数组和集合并不相同,因此无法正确收到。为了解决这个问题就还得拿出之前说到的 @RequestParam 来进行绑定参数,在前文中我们说到 @RequestParam 可以让传递的参数和接收的参数进行绑定,使得即使二者的名称不一样,也可以正确的接收参数。对于该注解的这个特性,用在这里也是非常适合的。

那么加收 @RequestParam 注解之后,就可以发现后端正确的接收到了参数

    @RequestMapping("/listParam")
    public String listParam(@RequestParam("listParam") ArrayList<String> listParam) {
        return "集合元素为:" + listParam;
    }

4.JSON传参(@RequestBody)

在上述种种传参方式中,我们会发现使用起来多多少少会有一点不方便。在企业开发中,为了避免各种各样参数传递带来的不便性,为了提供一种统一的数据格式传递方式,JSON应运而生。

JSON:JavaScript Object Notation 【JavaScript 对象表⽰法】

JSON是⼀种轻量级的数据交互格式. 它基于 ECMAScript (欧洲计算机协会制定的js规范)的⼀个⼦集,采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。--百度百科

简单来说:JSON就是⼀种数据格式,有⾃⼰的格式和语法,使⽤⽂本表⽰⼀个对象或数组的信息,因此JSON本质是字符串,主要负责在不同的语⾔中数据传递和交换,就像XML一样。

{
  "name": "张三",
  "age": 30,
  "isStudent": false,
  "hobbies": ["足球", "阅读", "旅行"],
  "education": {
    "university": "北京大学",
    "degree": "硕士",
    "graduationYear": 2024
  },
  "contact": {
    "email": "zhangsan@example.com",
    "phone": "1234567890"
  }
}

这段JSON数据描述了一个名为张三的人的信息,包括他的姓名、年龄、是否是学生、爱好、教育背景以及联系方式。

JSON数据由键值对组成,键和字符串值用双引号包围,数值直接表示,布尔值使用truefalse,数组用方括号[]表示,对象用花括号{}表示。

对于使用JSON数据格式传递进来的参数,我们可以将其解析拆分为我们需要的数据格式,如数组、集合、对象等。

Spring MVC框架也集成了JSON的转换⼯具, 我们可以直接使⽤, 来完成JSON字符串和Java对象的互转。本质上是 jackson-databind 提供的功能, Spring MVC框架中已经把该⼯具包引⼊了进来, 咱们直接使⽤即可。

如果不满意框架自带的JSON数据格式转化工具,也只需要在pom.xml中引入我们想使用的工具类即可,在各大开源社区和论坛中有着许多这样的JSON格式转化的工具包。

例如fastjson、jfire-codejson、struct2json、snack3等,这些项目都是开源的,并且支持不同的编程语言和功能。

笔者这里还是使用框架自带的JSON转化工具jackson-databind 来进行演示

public class JSONUtilTest {
    private static ObjectMapper objectMapper = new ObjectMapper();
    
    public static void main(String[] args) throws JsonProcessingException {
        //对象转JSON
        Person person = new Person();
        person.setName("zhangsan");
        person.setAge(18);
        person.setPasswd("123456");
        
        String json = objectMapper.writeValueAsString(person);
        
        System.out.println(json);
    }
}

同样的,我们也可以将JSON字符串转化为对象

//JSON转对象
String str = "{\"name\":\"zhangsan\",\"age\":18,\"passwd\":\"123456\"}";
Person person = objectMapper.readValue(str, Person.class);

 使用JSON进行数据传递有以下好处:

  • 简单易⽤: 语法简单,易于理解和编写,可以快速地进⾏数据交换
  • 跨平台⽀持: JSON可以被多种编程语⾔解析和⽣成, 可以在不同的平台和语⾔之间进⾏数据交换和传输
  • 轻量级: 相较于XML格式, JSON数据格式更加轻量级, 传输数据时占⽤带宽较⼩, 可以提⾼数据传输速度
  • 易于扩展: JSON的数据结构灵活,⽀持嵌套对象和数组等复杂的数据结构,便于扩展和使⽤
  • 安全性: JSON数据格式是⼀种纯⽂本格式,不包含可执⾏代码, 不会执⾏恶意代码,因此具有较⾼的安全性

在项目中要接收JSON传递的参数则需要使用 @RequestBody 这个注解,框架则会自动帮我们进行格式转化。

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

5.URL中的参数(@PathVariable)

对于上述的参数接收,我们基本上都素在请求体或者说是请求正文中获取的参数,但是很多时候请求的这个链接路径它本身也可以作为参数,对于这样的参数我们该怎么接收呢?

对于这样的参数,我们通常使用 @PathVariable 注解来接收,path variable表示路径变量,和字⾯表达的意思⼀样, 这个注解主要作⽤在请求URL路径上的数据绑定,使用该注解的时候需要注意在请求路径上设置一个占位符用来接收该位置的参数,对应下方代码的就是 {userId} ,然后将其通过@PathVariable 注解绑定到我们设置的变量上即可。

    @RequestMapping("/pathVariable/{userId}")
    public String pathVariable(@PathVariable("userId") Integer userId) {
        return "用户ID为:" + userId;
    }

6.上传文件(@RequestPart)

上传文件需要使用 MultipartFile 这个类

接收到文件后使用其内部的方法就可以保存到本地了

    @RequestMapping("/fileParam")
    public String fileParam(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        file.transferTo(new File("D:/Users/temp/" + originalFilename));
        return "接收到文件:" + originalFilename;
    }

 

 在我们的本地也确实是可以看见刚才上传的文件的

但是假如我们前后端传递的参数名不一致,就会导致无法接收这个文件

为了解决这样的问题,我们就可以使用 @RequestPart 注解来进行文件的绑定,就像之前用的@RequestParam一样

    @RequestMapping("/fileParam")
    public String fileParam(@RequestPart("file1") MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        file.transferTo(new File("C:/temp/" + originalFilename));
        return "接收到文件:" + originalFilename;
    }

7.Cookie传参(@CookieValue)

前文中提到SpringMVC是基于原生的Servlet技术发展的,那么对于Cookie的传参也是兼容了Servlet中的操作,即直接从请求的request中获取

    @RequestMapping("/Cookie1")
    public String getCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                System.out.println(c.getName() + ":" + c.getValue());
            }
        }
        return "Cookie获取成功";
    }

除此之外,我们还知道Cookie的传递其实也是键值对的形式,我们可以通过@CookieValue注解来直接获取Cookie中某个Key对应的Value,比如下面这段代码就可以获取一段Cookie中username的值

    @RequestMapping("/Cookie2")
    public String getCookie2(@CookieValue("username") String username) {
        System.out.println(username);
        return "Cookie获取成功";
    }

8.Session传参(@SessionAttribute)

对于Servlet中的方法,在这里都是兼容可以使用的,以下是简单设置session和获取的示例

    @RequestMapping("/setSession")
    public String setSession(HttpServletRequest request) {
        //参数为true,如果session对象不存在则创建一个,如果存在则直接返回
        HttpSession session = request.getSession(true);
        if (session != null) {
            session.setAttribute("name", "zhangsan");
            session.setAttribute("passwd", "123456");
        }
        return "设置Session成功";
    }
    
    @RequestMapping("/getSession")
    public String getSession(HttpServletRequest request) {
        //参数为false,如果session不存在则返回null
        HttpSession session = request.getSession(false);
        if (session != null) {
            System.out.println(session.getAttribute("name"));
            System.out.println(session.getAttribute("passwd"));
        }
        return "获取Session成功";
    }

还要一种更简单的写法,对于Session的获取也可以直接通过内置对象HttpSession获取

    @RequestMapping("/getSession2")
    public String getSession2(HttpSession session) {
        System.out.println(session.getAttribute("name"));
        System.out.println(session.getAttribute("passwd"));
        return "获取Session成功";
    }

同时我们还可以使用@SessionAttribute注解的方式继续简化,该注解可以直接获取到Session种的字段并且绑定到我们的变量上。同之前的注解一样,value对应具体要接收的参数,required表示该参数是否必传。

    @RequestMapping("/getSession3")
    public String getSession3(@SessionAttribute(value = "name", required = false) String name) {
        System.out.println(name);
        return "获取Session成功";
    }

 9.获取Header(@RequestHeader)

首先是使用Servlet中的方法

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

其次是使用Spring中的@RequestHeader注解

    @RequestMapping("/getHeader2")
    public String getHeader2(@RequestHeader("User-Agent") String header) {
        return "User-Agent" + header;
    }

▐ 响应 

对于一个后端程序,能够接收用户请求,能够接收用户传递的参数信息,如果还能根据业务逻辑做出响应,就已经可以满足绝大部分的开发需求了。

假如我们现在有一个录入信息的 html 静态页面,我们想要用户访问 /response/login 这个路径后就可以看见这个页面并且进行操作。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>个人信息</title>
    <style>
        div {
            width: 33%;
            margin: 0 auto;
        }
    </style>
</head>
<body>
<div>
    <h1>个人信息录入</h1>
    <form action="" method="post">
        姓名:<input type="text" name="name"><br><br>
        密码:<input type="password" name="password"><br><br>
        性别:<label><input type="radio" name="sex">男</label>
        <label><input type="radio" name="sex">女</label><br><br>
        年龄:<input type="number" name="age"><br><br>
        语言:<label><input type="checkbox" name="language" value="Java">Java</label>
        <label><input type="checkbox" name="language" value="C">C</label>
        <label><input type="checkbox" name="language" value="lua">lua</label>
        <label><input type="checkbox" name="language" value="Python">Python</label><br><br>
        照片:<input type="file" name="photo"><br><br>
        生日:<input type="date" name="brithday"><br><br>
        时间:<input type="time" name="time"><br><br>
        日期以及时间:<input type="datetime-local" name="datetime-local"><br><br>
        邮箱:<input type="email" name="email"><br><br>
        学历:<select name="degree">
        <option value="">请选择您的学历...</option>
        <option value="1">初中</option>
        <option value="2">高中</option>
        <option value="3">本科</option>
        <option value="4">硕士</option>
        <option value="5">博士</option>
    </select><br><br>
        个人简介:<textarea name="information"cols="30" rows="10"></textarea><br><br>
        <input type="hidden" name="id">
        <!-- 按钮 -->
        <input type="button" value="确认按钮"><br><br> <!-- 无意义 -->
        <input type="reset" value="重置">
        <input type="submit" value="提交">
    </form>
</div>
</body>
</html>

如果我们按照之前的方法去编码,可以完成需求吗?

@RequestMapping("/response")
@RestController
public class ResponseController {
    
    @RequestMapping("/login")
    public String login() {
        return "/login.html";//绝对路径,如果不加“/”就相对于是/response/login.html
    }
}

我们会发现程序将这个资源路径作为了文本数据返回到了浏览器

其实问题就出现在@RestController上,我们再打开它源码会发现@RestController是由@controller@ResponseBody组成的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

@Controller & @ResponseBody

在前文中我们大量使用的@RestController 其实主要是返回数据

@RestController  =  @Controller  +  @ResponseBody

  • @Controller : 定义⼀个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理.
  • @ResponseBody : 定义返回的数据格式为⾮视图, 返回⼀个 text/html 信息

如果想返回视图的话, 只需要把 @ResponseBody 去掉就可以了, 也就是 @Controller 。如下所示:

@RequestMapping("/response")
@Controller
public class ResponseController {
    @RequestMapping("/login")
    public String login() {
        return "/login.html";
    }
}

如果加上 @ResponseBody 注解, 该⽅法就会把 "/login.html" 当做⼀个数据返回给浏览器

@RequestMapping("/response")
@Controller
@ResponseBody
public class ResponseController {
    @RequestMapping("/login")
    public String login() {
        return "/login.html";
    }
}

 

如果观察源码,我们得知@ResponseBody既是类注解, ⼜是⽅法注解。如果作⽤在类上, 表⽰该类的所有⽅法, 返回的都是数据, 如果作⽤在⽅法上, 表⽰该⽅法返回的是数据。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}

如果⼀个类的⽅法⾥, 既有返回数据的, ⼜有返回⻚⾯的, 就把 @ResponseBody 注解添加到对应的⽅法上即可

@Controller
public class IndexController {
    @RequestMapping("/index")
    public Object index(){
        return "/index.html";
    }
    @RequestMapping("/returnData")
    @ResponseBody
    public String returnData(){
        return "该⽅法返回数据";
    }
}

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

    @RequestMapping("/returnHtml")
    @ResponseBody
    public String returnHtml() {
        return "<h1>Hello,HTML~</h1>";
    }

包括JSON等数据格式都是支持返回的

我们也可以通过 HttpServletResponse 这样的内置对象去响应的header

@RequestMapping(value = "/setHeader")
@ResponseBody
public String setHeader(HttpServletResponse response) {
 response.setHeader("MyHeader","MyHeaderValue");
 return "设置Header成功";
}



 本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/798387.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

7.13实训日志

上午 学习网络安全的过程中&#xff0c;我们深入了解了网络的不同层面和技术&#xff0c;从表层网络到深网再到暗网&#xff0c;以及涉及的产业分类和技术工具。这些知识不仅帮助我们理解网络的复杂性&#xff0c;还揭示了如何应对和防范各种网络威胁。 首先&#xff0c;我们…

查找PPT中某种字体的全部对应文字

本文章的目的是找到某种字体的文字&#xff0c;而不是替换某种字体的文字&#xff0c;也不是将某种字体全部替换为另外一种文字。 第一步&#xff1a;在PPT中按下ALTF11 出现以下窗口 第二步&#xff1a;点击插入->模块 第三步&#xff1a;将以下代码输入到窗体中 Sub F…

Java HashMap红黑树学习

Java HashMap红黑树学习 一、红黑树介绍二、红黑树的基本操作2.1 旋转2.1.1 左旋2.1.2 右旋 2.2 添加2.3 删除 一、红黑树介绍 &#xff08;1&#xff09;红黑树(Red-Black Tree&#xff0c;简称R-B Tree)&#xff0c;是一种特殊的平衡二叉查找树。 &#xff08;2&#xff09;节…

关于正点原子imx6ull串口实验,打开串口软件后无反应

我在某多多买了俩读卡器才1.3&#xff0c;不得不说真便宜。买的是2.0给我发的是3.0.具体是真假我也不太清楚。 反正连上之后发现烧写程序后一直串口没反应&#xff0c;但是串口显示的是绿标&#xff0c;也就代表硬件没问题。 然后我跟着按了几下依旧没啥反应&#xff0c;突然…

自学鸿蒙HarmonyOS的ArkTS语言<九>自定义弹窗组件CustomDialog及二次封装自定义弹窗

一、自定义弹窗 CustomDialog struct CustomDialogBuilder {controller: CustomDialogController new CustomDialogController({ // 注意写法builder: CustomDialogBuilder({})})// controller: CustomDialogController // 这种预览会报错cancel?: () > voidconfirm?: (…

用API实现商品sku抓取字段展示-淘宝sku区间价展示逻辑和规则分析

有卖家问我&#xff1a;我的链接里面有5个sku&#xff0c;都是不同的价格&#xff0c;为什么消费者看到的不是最低价呢&#xff1f; 这是因为淘宝平台商品价格的展示规则发生了变化&#xff0c;存在SKU区间价的产品&#xff0c;现在在搜索结果页面的曝光已经不是默认显示最低s…

51单片机学习——矩阵键盘控制led

前言介绍 按键控制LED亮灭 #include <REGX52.H> void main() {while(1){if(P3_40){P1_10;}else{P1_11;}}}按键控制led状态 #include <REGX52.H> void Delay(unsigned int xms) //11.0592MHz {unsigned char i, j;while(xms){i 2;j 199;do{while (--j);} while …

鸿蒙语言基础类库:【@system.battery (电量信息)】

电量信息 说明&#xff1a; 从API Version 6开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.batteryInfo]。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import battery from syste…

算法学习day12(动态规划)

一、不同的二叉搜索树 二叉搜索树的性质&#xff1a;父节点比左边的孩子节点都大&#xff1b;比右边的孩子节点都小&#xff1b; 由图片可知&#xff0c;dp[3]是可以由dp[2]和dp[1]得出来的。(二叉搜索树的种类和根节点的val有关) 当val为1时&#xff0c;左边是一定没有节点的…

获奖案例回顾|基于卫星遥感和无人机的水稻全流程风险减量项目

引言 在现代农业保险领域&#xff0c;技术创新是推动行业进步的关键。珈和科技与太平财险的合作&#xff0c;旨在利用先进的卫星遥感和无人机技术&#xff0c;解决传统农业保险面临的诸多挑战&#xff0c;从而提升保险效率和服务质量。本次分享的项目案例获得了《金融电子化》…

leetcode日记(37)旋转图像

方法是看评论区想出来的&#xff1a;先将矩阵转置&#xff0c;再将每一行逆转 class Solution { public: int n,m,l,k; struct bian{int u;int v;int d; }; void digui(int loc,int c[],vector<bian> bi,int now,int q,bool colour[],int& maxx,bool jg[]){if(q>…

利用宝塔安装一套linux开发环境

更新yum&#xff0c;并且更换阿里镜像源 删除yum文件 cd /etc/yum.repos.d/ 进入yum核心目录 ls sun.repo rm -rf * 删除之前配置的本地源 ls 配置阿里镜像源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 配置扩展包 wge…

8款值得收藏的App推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 值得一试的大众APP&#xff0c;它可能会给你的生活带来小小的改变。把下面的内容看完&#xff0c;我确信你一定会收获不少。 一、Todo清单——重…

Java之Stream流的笔记--手写版

Stream流通过讲集合或数组转换成链状流式的结构&#xff0c;简化了集合和数组进行排序、筛选、遍历、去重、统计等操作。主要包括创建流、中间操作、终结操作。若流中无终结操作&#xff0c;则中间操作不会执行&#xff1b;流是一次性的&#xff0c;使用完就会失效&#xff0c;…

【 香橙派 AIpro评测】烧系统运行部署LLMS大模型体验Jupyter Lab AI 应用样例(新手入门)

文章目录 一、引言⭐1.1下载镜像烧系统⭐1.2开发板初始化系统配置远程登陆&#x1f496; 远程ssh&#x1f496;查看ubuntu桌面&#x1f496; 远程向日葵 二、部署LLMS大模型2.1 快速启动&#x1f496;拉取代码&#x1f496;下载mode数据&#x1f496;启动模型对话 三、体验 内置…

SpringCloud02_consul概述、功能及下载、服务注册与发现、配置与刷新

文章目录 ①. Euraka为什么被废弃②. consul简介、如何下载③. consul功能及下载④. 服务注册与发现 - 8001改造⑤. 服务注册与发现 - 80改造⑥. 服务配置与刷新Refresh ①. Euraka为什么被废弃 ①. Eureka停更进维 ②. Eureka对初学者不友好,下图为自我保护机制 ③. 阿里巴巴…

多个版本JAVA切换(学习笔记)

多个版本JAVA切换 很多时候&#xff0c;我们电脑上会安装多个版本的java版本&#xff0c;java8&#xff0c;java11&#xff0c;java17等等&#xff0c;这时候如果想要切换java的版本&#xff0c;可以按照以下方式进行 1.检查当前版本的JAVA 同时按下 win r 可以调出运行工具…

Kafka基础入门-代码实操

Kafka是基于发布/订阅模式的消息队列&#xff0c;消息的生产和消费都需要指定主题&#xff0c;因此&#xff0c;我们想要实现消息的传递&#xff0c;第一步必选是创建一个主题&#xff08;Topic&#xff09;。下面我们看下在命令行和代码中都是如何创建主题和实现消息的传递的。…

MySql性能调优04-[MySql事务与锁机制原理]

MySql事务与锁机制原理 从undo与redo日志&#xff0c;理解事务底层ACID底层原理事务四大隔离级别事务底层锁机制和MVCC并发优化机制串行化底层实现机制读已提交和可重复读底层实现MVCC机制详解脏写问题(重要)读已提交&#xff1f;实现机制 BufferPool缓存与redo日志是如何提升事…

【AI大模型】李彦宏从“卷模型”到“卷应用”的深度解析:卷用户场景卷能给用户解决什么问题

文章目录 一、理解李彦宏的发言1.1 李彦宏的核心观点1.2 背景分析 二、技术发展&#xff1a;从辨别式到生成式2.1 辨别式AI技术2.2 生成式AI技术2.3 技术发展的挑战 三、“卷应用”&#xff1a;聚焦实际应用与价值3.1 应用为王3.2 技术落地的关键 四、“卷场景”&#xff1a;多…