
🎬 那我掉的头发算什么 :个人主页
🔥 个人专栏 : 《javaSE》《数据结构》《数据库》《javaEE》
⛺️待到苦尽甘来日

引言
在上一篇内容中,我们聚焦于 SpringMVC 的请求相关知识点。当时示例代码里的所有接口方法,返回类型都直接定义为 String------ 这是因为我们并未对接口的响应结果做任何规范化约束。而通过本篇内容的学习,相信大家对 SpringMVC 的核心机制,会有更深入、更体系化的理解!
文章目录
- 引言
- 响应
-
- 1.返回静态页面
- 2.返回HTML代码片
-
- [响应中的 Content-Type 常见取值](#响应中的 Content-Type 常见取值)
- 3.返回JSON
- 4.设置状态码
- 5.设置Header
- 拓展:lombok
响应
其实响应的内容丰富多彩,可以是页面也可以是数据,我们还可以更改响应的状态码Header头等。
1.返回静态页面
java
@RestController
@RequestMapping("/resp")
public class RespController {
@RequestMapping("/r1")
public String r1(){
return "html/title.html";
}
}
按照我们之前的理解,此时这段代码应该像上面这样写。但是很遗憾,这种写法不对。。。
资源前的"/"问题
首先,return "html/title.html"这里的资源前面应该加/。
绝对路径(以 / 开头):相当于 "从城市的市中心(Web 根目录)出发找地址",路径起点是固定的,不受当前位置影响。
相对路径(不以 / 开头):相当于 "从你现在站的位置(当前请求路径)出发找地址",路径起点是动态的。
而此时不加/,相当于地址是/resp/html/title.html,此时会出现404错误。
加了/,会自动找到static/html/title.html。
@Controller和@RestController
我们前面遇到@Controller和@RestController时我们只是简单地说,有了这个注解,Spring在扫描资源时才会扫描这个路径下的资源。在此时,这两者作用都是一样的,但是他们之间还有其他的不同之处。

首先,有意思的是,如果我们打开@RestController的源码会发现它的源码里有@Controller和@ResponseBody。
首先说结论:@RestController = @Controller + @ResponseBody
Controller注解单独使用表示返回视图,ResponseBody注解表示返回数据。但是Controller注解本身有扫描的作用,所以ResponseBody一般不能单独使用。而RestController就可以表示返回数据。
补充:@Controller 单独使用时,默认行为 是把方法返回值解析为「视图路径」(比如之前返回的/html/title.html);但@Controller 并不是 "只能返回视图"------ 只要在方法 / 类上加上@ResponseBody,它也能返回数据(JSON / 字符串等)。
因此,在类上加@Controller注解表示这个类默认行为是返回视图,加@RestController表示这个类中所有方法默认都返回数据。当然注解也可以加在方法上,这也是 Spring 灵活控制返回类型的核心方式:
1.在 @Controller 类中给方法加 @ResponseBody:单个方法返回数据
2. 在 @RestController 类中给方法加 @Controller 相关注解(几乎不用)
比如,我们修改代码:
java
@ResponseBody
@RequestMapping("/r2")
public String r2(){
return "/html/title.html";
}

2.返回HTML代码片
后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析
java
@ResponseBody
@RequestMapping("/returnHtml")
public String returnHtml(){
return "<h1>我是一个HTML代码片</h1>";
}


通过Fiddler观察响应结果, Content-Type 为 text/html
java
@ResponseBody
@RequestMapping(value ="/returnText",produces = "text/plain")
public String returnText(){
return "<h1>我是一个HTML代码片</h1>";
}
我们可以手动改变返回的Content-type


响应中的 Content-Type 常见取值
响应中的 Content-Type 常见取值有以下⼏种:
• text/plain
• text/html : body 数据格式是 HTML
• text/css : body 数据格式是 CSS
• application/javascript : body 数据格式是 JavaScript
• application/json : body 数据格式是 JSON
3.返回JSON
当方法的返回结果是一个对象或者一个对象的集合时,对象会被转成JSON字符串
Spring Boot 默认引入了 jackson-databind 依赖(spring-boot-starter-web 自带),它的核心类 ObjectMapper 负责把 Java 对象序列化(转成 JSON 字符串),对应的消息转换器是 MappingJackson2HttpMessageConverter。
java
@ResponseBody
@RequestMapping("returnJson1")
public Userinfo returnJson1(){
return new Userinfo("zhangsan",18,18);
}
@ResponseBody
@RequestMapping("returnJson2")
public List<Userinfo> returnJson2(){
List<Userinfo> list = new ArrayList<>();
list.add(new Userinfo("zhangsan",1,18));
list.add(new Userinfo("lis",1,18));
list.add(new Userinfo("wangwu",1,18));
return list;
}


注意:在JSON中,{}里面表示一个对象。

此时响应的Content-Type为application/json。
注意!类中必须加getter/setter,否则 Jackson 无法序列化字段,没法正常返回JSON
java
@ResponseBody
@RequestMapping(value = "returnJson2",produces = "text/plain")
public List<Userinfo> returnJson2(){
List<Userinfo> list = new ArrayList<>();
list.add(new Userinfo("zhangsan",1,18));
list.add(new Userinfo("lis",1,18));
list.add(new Userinfo("wangwu",1,18));
return list;
}
如果我们把这里的文本格式改成纯文本,此时程序会出现500错误:
Spring 默认的消息转换器(MappingJackson2HttpMessageConverter)只能把 Java 对象 / 集合序列化成application/json格式,无法直接把集合序列化成text/plain(纯文本)格式------ 没有对应的转换器能完成这个操作,因此抛出ConversionNotSupportedException(转换不支持异常),最终表现为 500 服务器内部错误。

4.设置状态码
java
@ResponseBody
@RequestMapping(value = "/setStatus",produces = "application/JSON")
public Userinfo setStatus(HttpServletResponse response){
response.setStatus(404);
return new Userinfo("zhangsan",18,18);
}


程序能正常打印,但是页面状态码是404.
5.设置Header
java
@ResponseBody
@RequestMapping(value = "/setHeader",produces = "application/JSON")
public Userinfo setHeader(HttpServletResponse response){
response.setHeader("myHeader","myHeader");
return new Userinfo("zhangsan",18,18);
}


拓展:lombok
作用
lombok是一个工具包,通过⼀些注解的⽅式, 可以帮助我们消除⼀些冗长代码, 使代码看起来简洁⼀些。
java
@Data
public class Person {
private int id;
private String name;
private String password;
}
一个@Data相当于帮我们自动创建好了getter,setter,toString,有参无参构造等方法。
可以观察加了 @Data 注解之后, Idea反编译的class文件
反编译是将可执行的程序代码转换为某种形式的高级编程语言, 使其具有更易读的格式. 反编译是⼀种逆向⼯程,它的作用与编译器的作用相反。
java程序运行原理:


想要观察加了@Data注解之后的代码实际是什么样子,可以这样做:
先使用Build Project预处理生成可运行文件

找到目标类

代码:
java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.bite.spring.demo;
import lombok.Generated;
public class LombokDemo {
private String name;
private String message;
private int age;
@Generated
public String getName() {
return this.name;
}
@Generated
public String getMessage() {
return this.message;
}
@Generated
public int getAge() {
return this.age;
}
@Generated
public void setName(final String name) {
this.name = name;
}
@Generated
public void setMessage(final String message) {
this.message = message;
}
@Generated
public void setAge(final int age) {
this.age = age;
}
@Generated
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof LombokDemo)) {
return false;
} else {
LombokDemo other = (LombokDemo)o;
if (!other.canEqual(this)) {
return false;
} else if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$message = this.getMessage();
Object other$message = other.getMessage();
if (this$message == null) {
if (other$message != null) {
return false;
}
} else if (!this$message.equals(other$message)) {
return false;
}
return true;
}
}
}
@Generated
protected boolean canEqual(final Object other) {
return other instanceof LombokDemo;
}
@Generated
public int hashCode() {
int PRIME = 59;
int result = 1;
result = result * 59 + this.getAge();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $message = this.getMessage();
result = result * 59 + ($message == null ? 43 : $message.hashCode());
return result;
}
@Generated
public String toString() {
String var10000 = this.getName();
return "LombokDemo(name=" + var10000 + ", message=" + this.getMessage() + ", age=" + this.getAge() + ")";
}
}
而在编译器中我们的代码只有短短这几行:
java
package com.bite.spring.demo;
import lombok.Data;
@Data
public class LombokDemo {
private String name;
private String message;
private int age;
}
如何引入Lombok依赖?
1.手动加入依赖
java
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

不用担心没有版本号问题,Spring Boot 通过「依赖管理(Dependency Management)」预定义了大量常用依赖的版本号,你引入这些依赖时无需手动指定版本,会自动使用 Spring Boot 官方适配的稳定版本。
2.去官网找到Lombok依赖并下载
网址:https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.38

直接复制加到pom文件中即可
3.使用EditStarters插件

下载后重启idea,然后按照下面的步骤操作即可:



以上就是本篇博客全部内容!