需求: 界面如上图所示👆 1. 输入留言信息, 点击提交. 后端把数据存储起来. 2. 页面展示输入得表白墙的信息.
码云链接: 薯条不要番茄酱
Ⅰ 发布留言 url : /message/publish . param(参数) : from,to,say . return : true / false .
Ⅱ 查询留言 url : /message/getList. param : 无 return : form 对 to 说了 say
Ⅰ 写代码之前,介绍一个新的工具包 lombok.
Lombok是一个Java工具库,通过添加注解的方式简化Java的开发. ① 引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
② 使用 lombok 通过一些注解的方式, 可以帮助我们消除一些冗长代码, 使代码看起来简洁一些. 就比如: 上面写的 Student 对象, 无需自己写 get , set , toString方法. 只需写一个注解 @Data就可以了.
import lombok.Data;
@Data
public class Student {
private String name;
private Integer id;
private int age;
}
③ 原理解释 加了 @Data 注解之后, Idea 反编译的class文件.
这不是真正的字节码文件, 而是Idea根据字节码进行反编译后的文件. 反编译是将可执行的程序代码转换为某种形式的高级编程语言, 使其具有更易读的格式. 反编译是一种逆向工程,它的作用与编译器的作用相反.
④ 更多使用 如果觉得 @Data生成的方法太多, lombok 也提供了一些更精细粒度的注解.
Ⅱ 更快捷引入依赖
① 安装插件EditStarter, 重启Idea.
② 在pom.xml 文件中, 单击右键, 选择Generate, 操作如下图所示
③ 进入 Edit Starters的编辑界面, 添加对应依赖即可.
Ⅲ 实现服务器代码 定义留言对象
import lombok.Data;
@Data
public class MessageInfo {
private String from;
private String to;
private String say;
}
创建 MessageController 类
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 {
List<MessageInfo> messageInfos = new ArrayList<>();
@RequestMapping("/publish")
public Boolean publish(MessageInfo messageInfo) {
//校验信息
if(!StringUtils.hasLength(messageInfo.getFrom())
|| !StringUtils.hasLength(messageInfo.getTo())
|| !StringUtils.hasLength(messageInfo.getSay())) {
return false;
}
//把信息存起来方便下一个方法获取
messageInfos.add(messageInfo);
return true;
}
@RequestMapping("/getList")
public List<MessageInfo> getList() {
return messageInfos;
}
}
前端页面代码
<!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>
function submit() {
$.ajax({
url: "/message/publish",
type: "get",
data: {
from: $("#from").val(),
to: $("#to").val(),
say: $("#say").val()
},
//http响应成功
success:function(result) {
if(result == false) {
alert("输入不合法");
}else {
//展示信息
//1. 构造节点
var divE = "<div>"+from+ "对" + to + "说:" + say + "</div>";
//2. 把节点添加到页面上
$(".container").append(divE);
//3. 清空输入框的值
$("#from").val("");
$("#to").val("");
$("#say").val("");
}
}
});
}
</script>
</body>
</html>
需求: 1. 登录: 用户输入账号,密码完成登录功能. 2. 列表展示: 展示图书.
创建新项目,引入依赖.
将前端代码复制到 static 目录下.
先实现其中的两个功能: 用户登录 和 图书列表展示.
需求分析:
1. 用户登录 url : /user/login param : userName 和 password return : String(提示) 2. 图书列表展示 1. url : /book/getBookList 2. param : 无 3. return : 图书列表
创建图书类
import lombok.Data;
import java.math.BigDecimal;
@Data
public class BookInfo {
private Integer id;
private String bookName;
private String author;
private Integer num;
private BigDecimal price;
private String publishName;
private Integer status;//对于可枚举的属性(以后可能会改的属性),通常用数字表示: 1-可借阅, 2-不可借阅
private String statusCN;
}
创建UserController, 实现登录验证接口.
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/login")
public String login(String userName, String password, HttpSession session) {
//校验用户信息是否合法.
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return "用户名或密码为空";
}
//判断用户名和密码是否正确
//理论上应该从数据库中获取, 但是目前还没学习 mybatis, 所以先这么写.
if(!"admin".equals(userName) || !"admin".equals(password)) {
return "用户名或密码错误";
}
session.setAttribute("userName",userName);
return "";
}
}
创建BookController, 获取图书列表
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;
@RequestMapping("/book")
@RestController
public class BookController {
@RequestMapping("/getBookList")
public List<BookInfo> getBookList() {
List<BookInfo> bookInfos = new ArrayList<>();
//mock(模拟) 数据
for (int i = 1; i <= 15; i++) {
BookInfo bookInfo = new BookInfo();
bookInfo.setId(i);
bookInfo.setBookName("图书"+i);
bookInfo.setAuthor("作者"+i);
bookInfo.setNum(i*2+1);
bookInfo.setPrice(new BigDecimal(i*3));
bookInfo.setPublishName("出版社"+i);
if(i % 5 == 0) {
bookInfo.setStatus(2);
bookInfo.setStatusCN("不可借阅");
}else {
bookInfo.setStatus(1);
bookInfo.setStatusCN("可借阅");
}
bookInfos.add(bookInfo);
}
return bookInfos;
}
}
登录页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<div class="container-login">
<div class="container-pic">
<img src="pic/computer.png" width="350px">
</div>
<div class="login-dialog">
<h3>登陆</h3>
<div class="row">
<span>用户名</span>
<input type="text" name="userName" id="userName" class="form-control">
</div>
<div class="row">
<span>密码</span>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="row">
<button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
url : "/user/login",
type : "post",
data : {
userName : $("#userName").val(),
password : $("#password").val(),
},
success:function(result) {
if(result != "") {
alert("用户名或密码错误,请重新输入");
}else {
location.href = "book_list.html";
}
}
});
}
</script>
</body>
</html>
图书列表展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图书列表展示</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/list.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script src="js/jq-paginator.js"></script>
</head>
<body>
<div class="bookContainer">
<h2>图书列表展示</h2>
<div class="navbar-justify-between">
<div>
<button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
<button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>
</div>
</div>
<table>
<thead>
<tr>
<td>选择</td>
<td class="width100">图书ID</td>
<td>书名</td>
<td>作者</td>
<td>数量</td>
<td>定价</td>
<td>出版社</td>
<td>状态</td>
<td class="width200">操作</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="demo">
<ul id="pageContainer" class="pagination justify-content-center"></ul>
</div>
<script>
getBookList();
function getBookList() {
$.ajax({
url:"/book/getBookList",
type:"get",
success:function(books) {
var finnalHtml = "";
for(var book of books) {
finnalHtml += '<tr>';
finnalHtml += '<td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>';
finnalHtml += '<td>'+book.id+'</td>';
finnalHtml += '<td>'+book.bookName+'</td>';
finnalHtml += '<td>'+book.author+'</td>';
finnalHtml += '<td>'+book.num+'</td>';
finnalHtml += '<td>'+book.price+'</td>';
finnalHtml += '<td>'+book.publishName+'</td>';
finnalHtml += '<td>'+book.statusCN+'</td>';
finnalHtml += '<td>';
finnalHtml += '<div class="op">';
finnalHtml += '<a href="book_update.html?bookId='+book.id+'">修改</a>';
finnalHtml += '<a href="javascript:void(0)" onclick="deleteBook('+book.id+')">删除</a>';
finnalHtml += '</div>';
finnalHtml += '</td>';
finnalHtml += '</tr>';
}
$("tbody").html(finnalHtml);
}
});
}
//翻页信息
$("#pageContainer").jqPaginator({
totalCounts: 100, //总记录数
pageSize: 10, //每页的个数
visiblePages: 5, //可视页数
currentPage: 1, //当前页码
first: '<li class="page-item"><a class="page-link">首页</a></li>',
prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',
next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',
last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',
page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',
//页面初始化和页码点击时都会执行
onPageChange: function (page, type) {
console.log("第"+page+"页, 类型:"+type);
}
});
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
alert("删除成功");
}
}
function batchDelete() {
var isDelete = confirm("确认批量删除?");
if (isDelete) {
//获取复选框的id
var ids = [];
$("input:checkbox[name='selectBook']:checked").each(function () {
ids.push($(this).val());
});
console.log(ids);
alert("批量删除成功");
}
}
</script>
</div>
</body>
</html>
通过上面的练习, 我们学习了Spring MVC简单功能的开发, 但是我们也发现了一些问题。 目前我们程序的代码有点"杂乱", 然而当前只是"一点点功能"的开发. 如果我们把整个项目功能完成代码会更加的"杂乱无章"(文件乱, 代码内容也乱)。 基于此, 咱们接下来学习应用分层.
Ⅰ 什么是应用分层? 应用分层 是一种软件开发设计思想, 它将应用程序分成N个层次, 这N个层次分别负责各自的职责, 多个层次之间协同提供完整的功能. 根据项目的复杂度, 把项目分成三层, 四层或者更多层. 常见的MVC设计模式, 就是应用分层的一种具体体现. Ⅱ 应用分层的目的 在最开始的时候,为了让项目快速上线,我们通常是不考虑分层的. 但是随着业务越来越复杂,大量的 代码混在一起,会出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵动全局的问题。 所以学习对项进行分层就是我们程序员的必修课了. Ⅲ 如何分层 “MVC” 就是把整体的系统分成了 Model(模型), View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔开,并且通过控制器连接起来,很好地实现 了表现和逻辑的解耦,是一种标准的软件分层架构。
目前更主流的开发方式是 “前后端分离” 的方式, 后端开发工程师不再需要关注前端的实现, 对于Java后端开发者, 又有了一种新的分层架构: 把整体架构分为表现层、业务逻辑层和数据层. 这种分层方式也称之为"三层架构". 1. 表现层: 就是展示数据结果和接受用户指令的,是最靠近用户的一层; 2. 业务逻辑层: 负责处理业务逻辑, 里面有复杂业务的具体实现; 3. 数据层: 负责存储和管理与应用程序相关的数据。
按照上面的层次划分, Spring MVC 站在后端开发人员的角度上, 也进行了支持, 把上面的代码划分为三个部分:
请求处理、响应数据:负责,接收页面的请求,给页面响应数据. 逻辑处理: 负责业务逻辑处理的代码. 数据访问: 负责业务数据的维护操作,包括增、删、改、查等操作. 这三个部分, 在Spring的实现中, 均有体现:
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。 Service:业务逻辑层。处理具体的业务逻辑。 Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
使用上面的分层思想, 来对代码进行改造。 先创建对应的包路径, 并把代码移到对应的目录 com.fhao.book.controller com.fhao.book.service com.fhao.book.dao com.fhao.book.model
在dao 目录下创建 BookDao文件 在service 目录下创建 BookService文件:
将 BookController 中的代码改写成👇:
@RequestMapping("/getBookList")
public List<BookInfo> getBookList() {
BookService bookService = new BookService();
return bookService.getBookList();
}
BookDao文件 中的代码:
public List<BookInfo> mockData() {
//理论上该方法应该从数据库获取, 此处先mock 数据.
List<BookInfo> bookInfos = new ArrayList<>();
for (int i = 1; i <= 15; i++) {
BookInfo bookInfo = new BookInfo();
bookInfo.setId(i);
bookInfo.setBookName("图书"+i);
bookInfo.setAuthor("作者"+i);
bookInfo.setNum(i*2+1);
bookInfo.setPrice(new BigDecimal(i*3));
bookInfo.setPublishName("出版社"+i);
if(i % 5 == 0) {
bookInfo.setStatus(2);
bookInfo.setStatusCN("不可借阅");
}else {
bookInfo.setStatus(1);
bookInfo.setStatusCN("可借阅");
}
bookInfos.add(bookInfo);
}
return bookInfos;
}
BookService文件 中的代码
public List<BookInfo> getBookList() {
BookDao bookDao = new BookDao();
List<BookInfo> bookInfos = bookDao.mockData();
for(BookInfo bookInfo : bookInfos) {
if(bookInfo.getStatus() == 2) {
bookInfo.setStatusCN("不可借阅");
}else {
bookInfo.setStatusCN("可借阅");
}
}
return bookInfos;
}
1. 降低层与层之间的依赖, 结构更加的明确, 利于各层逻辑的复用. 2. 开发人员可以只关注整个结构中的其中某一层, 极大地降低了维护成本和维护时间. 3. 可以很容易的用新的实现来替换原有层次的实现. 4. 有利于标准化.
学习Spring MVC, 其实就是学习各种Web开发需要用的到注解。 a. @RequestMapping: 路由映射
b. @RequestParam: 后端参数重命名
c. @RequestBody: 接收JSON类型的参数
d. @PathVariable: 接收路径参数
e. @RequestPart: 上传文件
f. @ResponseBody: 返回数据
g. @CookieValue: 从Cookie中获取值
h. @SessionAttribute: 从Session中获取值
i. @RequestHeader: 从Header中获取值
j. @Controller: 定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理. 默认返回 视图.
k. @RestController: @ResponseBody + @Controller 返回数据
Cookie 和Session都是会话机制, Cookie是客户端机制, Session是服务端机制. 二者通过 SessionId来关联. Spring MVC内置 HttpServletRequest 和HttpServletResponse 两个对象. 需要使用时, 直接在方法中添加对应参数即可, Cookie 和 Session 可以从HttpServletRequest 中来获取, 也可以直接使用 HttpServletResponse 设置Http响应状态码.