【Spring MVC】手动做出小网页的最后一步——学会SpringMVC响应


🎬 那我掉的头发算什么个人主页
🔥 个人专栏 : 《javaSE》《数据结构》《数据库》《javaEE》

⛺️待到苦尽甘来日


引言

在上一篇内容中,我们聚焦于 SpringMVC 的请求相关知识点。当时示例代码里的所有接口方法,返回类型都直接定义为 String------ 这是因为我们并未对接口的响应结果做任何规范化约束。而通过本篇内容的学习,相信大家对 SpringMVC 的核心机制,会有更深入、更体系化的理解!

文章目录

响应

其实响应的内容丰富多彩,可以是页面也可以是数据,我们还可以更改响应的状态码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,然后按照下面的步骤操作即可:


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

相关推荐
小楼v2 小时前
常见的java线程并发安全问题八股
java·后端·线程·并发安全
齐 飞2 小时前
springboot发送邮件
java·spring boot·后端
Hello eveybody2 小时前
Java发明者介绍
java·开发语言
Micro麦可乐2 小时前
最新Spring Security实战教程(十五)快速集成 GitHub 与 Gitee 的社交登录
java·spring boot·spring·gitee·github·spring security·社交登陆
u0104058362 小时前
企业微信会话存档API对接中的敏感信息审计日志架构(Java版)
java·架构·企业微信
HalvmånEver2 小时前
Linux:信号捕捉上(信号三)
linux·运维·服务器
Voyager First2 小时前
ApereoCas学习系列一——从github克隆cas-server源码并启动
java
像少年啦飞驰点、2 小时前
Spring Boot 从入门到实践:快速构建一个 RESTful API 服务
java·spring boot·后端开发·快速入门·restful api·编程小白
编程彩机2 小时前
互联网大厂Java面试:从Spring Cloud到Kafka的技术场景深度解析
java·spring cloud·微服务·kafka·技术面试