文章目录
SpringMVC
响应
返回静态页面
- 返回静态页面
1.元注解:可以被其他注解使用的叫做元注解
@Target:表示注解修饰的对象
{ElementType.TYPE}表示可以修饰类和接口
@Documnted:文档,比如jdk有很多的注释,表示要不要保留这些注释
2.@RestController包括:@Controller和@ResponseBody
现在@RestController被用来返回数据了
3.@Controller的作用:告诉Spring,帮我们管理这个代码,我们后续访问时,才能访问到,才能使用我们写的代码,不写这个注解,Spring是不管这个代码的
注解的生命周期:
举个例子,写一个简单的静态页面(会打印index.html文件上的内容):
如果有多个注解,注解的顺序是不影响结果的
java
@RequestMapping("/return")
//@RestController // 返回数据
@Controller // 返回视图
public class ReturnController {
@RequestMapping("/index")
public String returnIndex(){
// 使用@RestController时返回数据
// 这时是返回这个数据,不是返回这个页面
return "/index.html";
}
}
返回数据@Responsebody
- @Responsebody可以修饰类,也可以修饰方法,修饰类的时候,表示这个类下的所有方法,返回的均为数据
修饰方法时,表示该方法返回的是数据 - 如果一个类中的所有方法返回的都是数据,我们可以把这个注解加在类上
返回一个数据
java
@ResponseBody
@RequestMapping("/returnData")
public String returnData(){
return "返回的是一个数据";
}
返回html的代码片段
- 返回html的代码片段
java
// 返回代码片段
@ResponseBody
@RequestMapping("/returnHtml")
public String returnHtml(){
return "<h1>这是一个代码</h1>";
}
返回一个JSON
- 返回一个JSON
java
// 返回的是一个json对象
@ResponseBody
@RequestMapping("/returnJson")
public Person returnJson(){
Person person = new Person();
person.setId(1);
person.setAge(5);
person.setName("zhangsan");
return person;
}

- 返回一个Map对象
java
// 返回的是一个json对象
@ResponseBody
@RequestMapping("/returnJson")
public Person returnJson(){
Person person = new Person();
person.setId(1);
person.setAge(5);
person.setName("zhangsan");
return person;
}
@ResponseBody
@RequestMapping("/returnMap")
public Map<String,String> returnMap(){
Map<String,String> map = new HashMap<>();
map.put("k1","v1");
map.put("k2","v2");
map.put("k3","v3");
return map;
}
返回一个对象时,会自动帮我们设置为json格式,所以说返回的类型会被自动设置
设置状态码
- 状态码不影响页面的展示(显示的还是代码返回的内容,只是使用fiddler抓包时看到响应是401)
java
@ResponseBody
@RequestMapping("/returnStaus")
public String returnStaus(HttpServletResponse response){
// 401通常表示没有登录
response.setStatus(401);
return "设置状态码";
}

设置Header
-
RequestMapping
method用来设置(限制)请求的方式
下面这些方式通常都不会使用,限制的太多了
-
使用produces来设置Header返回的类型
java
// 通过produces改变返回类型
@ResponseBody
@RequestMapping(value = "/r1",produces = "application/json; charset=utf-8")
public String r1(HttpServletResponse response){
// 可以设置header
// header是键值对形式的
response.setHeader("myhead","myhead");
return "{'OK',1}";
}
总结
- 对上面内容的总结:
综合性练习
- 练习前端和后端的交互过程,请求,响应的处理
1. 加法计算器
- 前端代码(calc.html):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">
<h1>计算器</h1>
数字1:<input name="num1" type="text"><br>
数字2:<input name="num2" type="text"><br>
<input type="submit" value=" 点击相加 ">
</form>
</body>
</html>
- 后端代码:
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/calc")
@RestController
public class CalcController {
@RequestMapping("/sum")
public String sum(Integer num1,Integer num2){
// 这就是打印日志
System.out.println("--------------------sum");
Integer sum = num1 + num2;
return "计算的结果为: " + sum;
}
}
- 如果报错了,该如何定位错误位置:
1.先定位前端还是后端问题
通过日志
(1) 前端:F12 看控制台
(2) 后端:接口,控制台日志
检测请求是否到达后端的方法:
(1) 可以进行抓包,看请求的路径
(2) 可以在后端进行打印信息,如果请求到了就会打印信息,否则不会打印信息(打印日志)
(3) 后端可以通过url或者是postman进行接口的测试(测试接口)
测试接口:
2. 用户登陆
- 用户登录时,前端存在缓存问题,还是显示之前的页面信息,那么我们该如何清理缓存?
显示的不是登录后,用户的信息,而是显示之前这个html中的信息
可以点击maven中的clean清除里面的缓存,直接运行代码里面就包含了打包的工作,浏览器中也是存在缓存的,可能还需要清理浏览器中的缓存(清除浏览器的记录就可以了)
-
写代码之前做的工作:
-
为什么登录时要设置Session的信息?
-
可以先测试后端的接口(后端的接口和前端的代码是没有关系的),然后再写前端的代码
-
前端的代码:
登录的逻辑:
login.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text" id="userName"><br>
密码:<input name="password" type="password" id="password"><br>
<input type="button" value="登录" onclick="login()">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
function login() {
console.log("登录...");
$.ajax({
url: "/user/login",
type: "post",
data:{
"userName": $("#userName").val(),
"password": $("#password").val()
},
success:function(result){
if(result){
location.href = "/index.html";
// location.assign();
}else{
alert("密码错误");
}
}
});
}
</script>
</body>
</html>
登录成功后,进行页面跳转,显示用户名信息:
index.html
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>用户登录首页</title>
</head>
<body>
登录人: <span id="loginUser"></span>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
//页面加载时, 就去调用后端请求
$.ajax({
url: "/user/getUserInfo",
type:"get",
success:function(username){
$("#loginUser").text(username);
}
});
</script>
</body>
</html>
- 后端的代码:
java
package com.example.demo.controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@RequestMapping("/user")
// 返回的是信息就使用这个注解
@RestController
public class UserController {
// 检测是否登录成功
@RequestMapping("/login")
public Boolean login(String userName, String password, HttpSession session){
// 1. 检验输入的用户名和密码是合法的(校验参数的合法性)
// if(usename == null || usename.length() == 0 || password == null ||
// password.length() == 0){
// return false;
// }
// Spring也提供了一种方式检验用户名和密码是否和法的
// StringUtils.hasLength()检验字符串是否有长度
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
return false;
}
// 2. 进行用户名和密码的校验
if("admin".equals(userName) && "admin".equals(password)){
// 设置Session,存储用户这次登录的信息
session.setAttribute("usename","admin");
return true;
}
return false;
}
// 第一种写法:
// 如果登录成功,返回登录的用户的信息
// @RequestMapping("getUserInfo")
// // HttpSession session 没有session,会自动创建一个session
// public String getUserInfo(HttpSession session){
// // 从Session中获取用户的登录名
// String usename = (String)session.getAttribute("usename");
//
// return usename;
// }
// 第二种写法:
@RequestMapping("/getUserInfo")
public String getUserInfo(HttpServletRequest request){
// 从Session中获取用户的登录名
// 没有Session,就不要创建一个Session
HttpSession session = request.getSession(false);
String usename = null;
if(session != null) {
usename = (String) session.getAttribute("usename");
}
return usename;
}
}
展示效果:
登录后进行跳转页面:
项目如何debug
- 先打一个断点
- 然后点击这个小虫子,进行启动代码
断点打成功,会有一个√号

- 后面步骤的调试项目和我们平常调试代码是一样的
3. 留言板
- 前端没有保存数据的功能,后端把数据保存下来(保存在内存,数据库,文件中等等...)
为了让留言板刷新内容不会消失
- 接口定义
(1) 提交留言(给服务器)
/message/publish
参数:MessageInfo(from,to,message)
返回结果:true/false
(2) 查看所有留言(后端显示所有留言)
/message/getMessageList
参数:无
返回结果:List< MessageInfo >
- lombok:工具包
(1) 作用:如果一个类中有许多的变量,都需要get和set方法来设置,那就会很麻烦,这时使用lombok,就可以不用写这些get和set方法了
(2) 对于不熟悉的包,如何选择maven版本,建议选择次新版本(使用频率比较高的版本)

(3) @Data这个注解帮我们写了get方法和set方法,这个工具可以针对所有属性写get和set方法,也可以单独对某个属性写get和set方法
单独对某个属性写get和set方法
SpringBoot帮我们管理了这些包,所以应该集成了这些包,安装一个插件就可以使用插件导入这个包了,就不需要从Maven仓库中导入这个包了
lombok
-
lombok中的一些注解
-
@ToString:自动帮我们写了ToString方法
留言板
- 为什么前端校验之后,后端还需要校验?
可能不是通过前端页面发起的请求,可能是一个非法的请求,这是最重要的原因

VSCode格式化:alt + shift + F,进行代码的对齐
- 前端代码:
messagewall.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
.container {
width: 350px;
height: 300px;
margin: 0 auto;
/* border: 1px black solid; */
text-align: center;
}
.grey {
color: grey;
}
.container .row {
width: 350px;
height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container .row input {
width: 260px;
height: 30px;
}
#submit {
width: 350px;
height: 40px;
background-color: orange;
color: white;
border: none;
margin: 10px;
border-radius: 5px;
font-size: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>留言板</h1>
<p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
<div class="row">
<span>谁:</span> <input type="text" name="" id="from">
</div>
<div class="row">
<span>对谁:</span> <input type="text" name="" id="to">
</div>
<div class="row">
<span>说什么:</span> <input type="text" name="" id="say">
</div>
<input type="button" value="提交" id="submit" onclick="submit()">
<!-- <div>A 对 B 说: hello</div> -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
//页面加载时, 请求后端, 获取留言列表
$.ajax({
url: "/message/getMessageInfo",
type: "get",
success: function (messages) {
for (var m of messages) {
//2. 拼接节点的 html
var divE = "<div>" + m.from + "对" + m.to + "说:" + m.message + "</div>";
//3. 把节点添加到页面上
$(".container").append(divE);
}
}
});
function submit() {
//1. 获取留言的内容
var from = $('#from').val();
var to = $('#to').val();
var say = $('#say').val();
if (from == '' || to == '' || say == '') {
return;
}
//提交留言
$.ajax({
url: "/message/publish",
type: "post",
data: {
"from": from,
"to": to,
"message": say
},
success: function (result) {
if (result) {
//添加成功
//2. 拼接节点的 html
var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
//3. 把节点添加到页面上
$(".container").append(divE);
//4. 清空输入框的值
$('#from').val("");
$('#to').val("");
$('#say').val("");
} else {
//添加失败
alert("留言发布失败");
}
}
});
}
</script>
</body>
</html>
- 后端代码:
MessageController:
java
package com.example.demo.controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RequestMapping("/message")
@RestController
public class MessageController {
private List<MessageInfo> messageInfos = new ArrayList<>();
// 1. 提交留言
@RequestMapping("/publish")
public Boolean publishMessage(MessageInfo messageInfo){
// 1. 进行参数的校验
// hasLength表示没有为空
if(!StringUtils.hasLength(messageInfo.getFrom()) ||
!StringUtils.hasLength(messageInfo.getTo())||
!StringUtils.hasLength(messageInfo.getMessage())){
// 只要一个为空,就不发送
return false;
}
// 2.添加留言
messageInfos.add(messageInfo);
return true;
}
// 2. 查看留言
@RequestMapping("/getMessageInfo")
public List<MessageInfo> getMessageInfo(){
return messageInfos;
}
}
用到的类:
MessageInfo:
java
package com.example.demo.controller;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Date;
@Data
class MessageInfo {
private String from;
private String to;
private String message;
// private Date CreateTime;
}
效果展示:
4. 图书管理系统
-
目标(这里不是完全体的需求,后面会继续完善的):
-
定义前后端交互接口
(1) 登录
url:/user/login
参数:useName = ? & password = ?
响应:true / false
(2) 图书列表展示
url:/book/getBookList
参数:无
响应:List< BookInfo >
- 后端代码:
图书类:BookInfo
java
package com.example.book2;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class BookInfo {
private Integer id;
private String bookName;
private String author;
private Integer count;
private BigDecimal price;
private String publish;
private Integer status;// 1表示可借阅, 2表示不可借阅
private String statusCN;// 图书的中文状态说明
}
登录:
UserController:
java
package com.example.book2;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/login")
public Boolean login(String useName, String password, HttpSession session){
// 1. 校验参数
// useName和password只要一个为空就返回false
if(!StringUtils.hasLength(useName) || !StringUtils.hasLength(password)){
return false;
}
/*
useName.equasl("admin"),如果useName为空时,会报空指针异常
这是一个开发习惯
*/
// 2. 验证账号,密码是否正确
if("admin".equals(useName) && "admin".equals(password)){
// 账号密码正确
// 存储Session,把用户名存起来
session.setAttribute("useName",useName);
return true;
}
return false;
}
}
图书列表展示:
BookController:
java
package com.example.book2;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RequestMapping("/book")
@RestController
public class BookController {
@RequestMapping("/getBookList")
public List<BookInfo> getBookList(){
// 1.获取图书的数据
// 2. 对图书的数据做一些修改
// 3. 返回数据
// mock 表示虚拟的,假的数据
List<BookInfo> bookInfos = mockData();
for(BookInfo bookInfo : bookInfos){
if(bookInfo.getStatus() == 1){
bookInfo.setStatusCN("可借阅");
}else{
bookInfo.setStatusCN("不可借阅");
}
}
return bookInfos;
}
private List<BookInfo> mockData() {
// 优化技巧:对于已知数据量大小或者是大概知道数据量大小,在创建list时,就指定初始化容量大小
List<BookInfo> bookInfos = new ArrayList<>(15);
for(int i = 0;i < 15;i++){
BookInfo bookInfo = new BookInfo();
bookInfo.setId(i);
bookInfo.setBookName("图书" + i);
bookInfo.setAuthor("作者" + i);
// 图书数量
bookInfo.setCount(new Random().nextInt(200));
bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
bookInfo.setPublish("出版社" + i);
bookInfo.setStatus(i % 5 == 0 ? 2 : 1);
bookInfos.add(bookInfo);
}
return bookInfos;
}
}
展示效果:
应用分层
- 三层架构
dao/db 通常指的是数据访问层中与数据库直接相关的组件
db中就是数据库相关的操作
- 代码中数据分层的表现:
- 那么这些代码的包也可以进行分层
model存的是数据对象(实体类)