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

news2024/12/23 5:05:43

目录

一.什么是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成功";
}



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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaSE 面向对象程序设计进阶 IO 压缩流 解压缩流

目录 解压缩流 压缩流 解压缩流 压缩包 压缩包里面的每一个文件在java中都是一个ZipEntry对象 把每一个ZipEntry按照层级拷贝到另一个文件夹当中 import java.io.*; import java.util.Date; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream;public cl…

清华计算几何-ConvexHull(凸包)-求极点InTriangle/ToLeft Test

ConvexHull(凸包) 凸包是什么 凸包是计算几何一个非常基础核心的概念。我理解的凸包就是给定一个点集合, 最外围的点的包围体就是凸包。如下所示: 极点(ExtremityPoint) 给定的点集合中, 如果一个点存在一条直线, 让其他所有点都在于该直线的同一侧, 则该点为极点。 非极点 …

【Java--数据结构】栈:不仅仅是数据存储,它是编程的艺术

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 栈 栈的方法介绍 入栈push 出栈pop和 瞄一眼peek 判空isEmpty和判满isFull 模拟实现栈 push入栈 pop出栈和peek 测试 使用泛型实现栈 测试 使用链表实现栈&#xff08…

本地部署免费AI大模型+知识库

利用Ollama部署本地大模型 一&#xff1a;终端实现与大模型对话 1.去Ollama官网下载 https://ollama.com/download2.下载之后按照提示一步步安装即可 3.WinR, 输入cmd,打开命令行提示符 输入&#xff1a;ollama run llama3运行大模型 4.使用其他开源大模型&#xff0c;比如…

基于门控循环单元(GRU)的数据回归预测

代码原理 基于GRU&#xff08;Gated Recurrent Unit&#xff09;的数据回归预测通常涉及多输入单输出的情况。以下是简单的原理及流程&#xff1a; 数据准备&#xff1a; 准备多个时间序列作为输入特征&#xff0c;每个时间序列可以表示不同的变量或特征。准备一个目标变量作…

算法学习day12(动态规划)

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

写真图片视频打赏系统源码全开源无加密

这是一款开源的写真图片及视频打赏系统源码&#xff0c;顾名思义他可以做写真图片打赏站也可以做视频打赏站&#xff0c;支付对接了易支付&#xff0c;拥有独立代理后台&#xff0c;全部源码无加密&#xff0c;另外也可以配合付费进群使用。支付扣量、域名防洪这些基本的就不介…

基于信号处理的PPG信号滤波降噪方法(MATLAB)

光电容积脉搏波PPG信号结合相关算法可以用于人体生理参数检测&#xff0c;如血压、血氧饱和度等&#xff0c;但采集过程中极易受到噪声干扰&#xff0c;对于血压、血氧饱和度测量的准确性造成影响。随着当今社会医疗保健技术的发展&#xff0c;可穿戴监测设备对于PPG信号的质量…

修正版头像上传组件

修正版头像上传组件 文章说明核心源码展示运行效果展示源码下载 文章说明 在头像剪切上传一文中&#xff0c;我采用div做裁剪效果&#xff0c;感觉会有一些小问题&#xff0c;在昨天基于canvas绘制的功能中改进了一版&#xff0c;让代码变得更简洁&#xff0c;而且通用性相对高…

Linux的load(负载)

负载(load)是Linux机器的一个重要指标&#xff0c;直观了反应了机器当前的状态。 在Linux系统中&#xff0c;系统负载是对当前CPU工作量的度量&#xff0c;被定义为特定时间间隔内运行队列中的平均线程数。 Linux的负载高&#xff0c;主要是由于CPU使用、内存使用、10消…

[UTCTF2020]babymips

水一篇 32位 c写的&#xff0c;长得比较丑陋 进入sub-401164函数 V7的数据可以得到 unsigned char ida_chars[] {0x62, 0x6C, 0x7F, 0x76, 0x7A, 0x7B, 0x66, 0x73, 0x76, 0x50, 0x52, 0x7D, 0x40, 0x54, 0x55, 0x79, 0x40, 0x49, 0x47, 0x4D, 0x74, 0x19, 0x7B, 0x6A, 0x…

大数据之路 读书笔记 Day5 数据同步遇到的问题与解决方案

回顾 Day 4 数据同步Day 3 无线客户端的日志采集 1. 分库分表的处理 分库分表&#xff08;Sharding&#xff09;是数据库水平扩展的一种策略&#xff0c;当单个数据库的性能和存储能力无法满足应用需求时&#xff0c;可以采用分库分表来分散数据和查询负载。它通常包括两个方面…

钡铼4G无线RTU助力智慧能源发展实现电网远程调控

随着全球对清洁能源和高效能源管理的需求日益增长&#xff0c;智慧能源技术正逐渐成为推动可持续发展的重要驱动力。在这一背景下&#xff0c;钡铼4G无线远程终端单元正在为智慧能源的发展和电网的远程调控提供强有力的支持。 钡铼4G无线RTU&#xff1a;智慧能源的神经网络 钡…

数据恢复篇:适用于 Android 的恢复工具

正在摆弄 Android 设备。突然&#xff0c;您意外删除了一张或多张图片。不用担心&#xff0c;您总能找到一款价格实惠的照片恢复应用。这款先进的软件可帮助 Android 用户从硬盘、安全数字 (SD) 或存储卡以及数码相机中恢复已删除的图片。 Android 上文件被删除的主要原因 在获…

Blender使用(二)点线面基本操作

Blender使用之点线面 1.编辑模式 tab键进行切换&#xff0c;为了方便菜单调出&#xff0c;可以设置键位映射为拖动时的饼菜单。 设置好后&#xff0c;按住tab键移动鼠标(注意不要点击鼠标)&#xff0c;即可弹出编辑菜单。 默认是点模式&#xff0c;在左上角可进行点线面的切换…

Linux系统OpenSSH出现漏洞(CVE-2024-6387)修复

CVE-2024-6387 是一个影响 OpenSSH 服务器&#xff08;sshd&#xff09;的严重远程代码执行&#xff08;RCE&#xff09;漏洞&#xff0c;它允许未经身份验证的攻击者在受影响的 Linux 系统上以 root 权限执行任意代码。此漏洞尤其危险&#xff0c;因为它可以在不需要任何用户交…

husky 和 lint-staged 构建代码项目规范

目录 前言 最简单的方法 过 scripts 来解决如果检测工具多&#xff0c;需要多次处理 通过 husky(哈士奇)来解决容易遗忘的问题 1. 安装 2. husky init 3. 试一试​ lint-stadge 只 lint 改动的 1. 安装 2. 修改 package.json 配置 3. 添加 npm 脚本: 4.使用 Husky…

ETL数据集成丨主流ETL工具(ETLCloud、DataX、Kettle)数据传输性能大PK

目前市面上的ETL工具众多&#xff0c;为了方便广大企业用户在选择ETL工具时有一个更直观性能方面的参考值&#xff0c;我们选取了目前市面上最流行的三款ETL工具&#xff08;ETLCloud、DataX、Kettle&#xff09;来作为本次性能传输的代表&#xff0c;虽然性能测试数据有很多相…

类和对象 中篇

类和对象 中篇 ​ 在上篇中&#xff0c;我们介绍了类的基础部分&#xff0c;本篇我们讲解C类的六大默认成员函数 ​ 所谓默认成员函数&#xff0c;就是我们不写编译器也会自动生成&#xff0c;自动调用的函数。而自动生成的函数对内置类型的成员不会处理(有些高版本编译器会…

FreeRTOS 入门 知识

什么是FreeRTOS FreeRTOS 是一个轻量级的实时操作系统&#xff08;RTOS&#xff09;&#xff0c;由 Richard Barry 在 2003 年开发&#xff0c;并且由亚马逊的 FreeRTOS 项目&#xff08;一个由 Amazon Web Services (AWS) 支持的开源项目&#xff09;进一步推动和发展。FreeR…