一:开发程序常见问题
(1)学会定位前后端问题
💗通过看日志,查看到底是前端问题还是后端问题
①前端:F12查看网页的控制台
②后端:测试后端接口参数;查看IDEA的控制台
(测试后端接口参数,即去访问后端写的路径,给参数,看看是否能返回正确结果)
🌟查看请求是否到达了后端,如果没有那就是前端问题,如果有那就是后端问题
(查看方法:在后端的第一行代码上随机打印,如果请求进来了,控制台会有打印的日志)
(2)缓存问题
💗比如说我的前端代码明明写得很完整,但是运行网址的时候没有出现效果,此时右键网页点击"查看页面源代码",发现是空的,那这就是缓存问题
🌟解决办法:点击IDEA右侧的Maven,选择Lifecycle目录下的clean,清除缓存
二:学会使用Lombok
(1)Lombok定义
💗Lombok是一个工具包,它提供了一组注解,用于自动生成常见的Java代码,如getter和setter方法、构造函数、equals和hashCode方法等
(2)Lombok好处
🌟使用Lombok可减少编写样板代码的工作量,提高开发效率,不用每次都setter和getter
(3)Lombok引入(中央仓库方法)
①打开中央仓库,搜索lombok
②选择1.18.24版本
(小tips:当我们选择不熟悉的第三方工具包时,不知道什么版本好,就选择使用人数最多的)
③复制Maven到pom.xml即可
(但凡导入了新的包,一定要记得刷新)
(4)Lombok引入(SpringBoot方法)
①点击左上角的File➜Settings➜Plugins➜搜索EditStarters
②点击install进行安装并重启IDEA即可
③右键pom.xml代码页面,选择Generate
④按照下图所示点击,然后点击OK即可
⑤此时在pom.xml上就有Lombok依赖了,然后刷新Maven即可
(这个连版本都没有,因为Spring已经帮我们搞定好了)
(5)Lombok常用注解
1.Lombok常用注解图
2.@Getter和@Setter
💗@Getter和@Setter在哪个属性上,就仅代表只有这个属性有getter和setter方法
💙比如:当我把@Getter放在from属性上,就代表只有from属性具有getter方法
3.@Data
💗使用@Data注解,放在类上,此时所有的属性都有getter和setter方法、构造函数、equals和hashCode方法等
💙比如:我这里有个MessageInfo类,里面的属性我不想手动setter和getter,就用@Data
(此时get和set在其他类直接用即可,比如getfrom、setmessage等等,不用再加什么setter和getter方法)
4.@ToString
💗使用@ToString注解,放在类上,此时说明整个类已经重写了toString方法
三:加法计算器
(1)前端代码
💗前端代码网盘链接:前端代码( 提取码:lzh7)
(2)准备工作
🌟①将加法计算器的前端代码引入到static目录下
🌟②初学阶段,建议每做一步就去运行后端然后访问一下,确保没有错误
(static目录下放的是静态文件,可以直接访问)
(3)约定前后端交互接口
(4)后端代码
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){ Integer sum = num1 + num2; return "计算结果为:"+sum; } }
(5)注意事项
(6)效果展示
四:用户登录
(1)前端代码
💗前端代码网盘链接:前端代码( 提取码:lzh7)
(2)准备工作
🌟①将用户登录的前端代码引入到static目录下
🌟②运行后端并访问,确保没有错误
(1)login.html
(2)index.html
(3)约定前后端交互接口
1.需求分析
①登录页面(login.html):通过账号和密码,校验输入的账号密码是否正确,并告知前端
②首页(index.html):告知前端当前的登录用户是谁
(如果当前已有用户登录,返回登录的账号;如果没有,返回空)
2.登录页面的交互接口
3.首页的交互接口
(4)后端代码
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) { //第一步:先去校验参数的合法性 //if(!username==null || username.length()==0 || password==null || password.length()==0){ //return false; //} if (StringUtils.hasLength(username) || !StringUtils.hasLength(password)){ return false; } //第二步:进行用户名和密码的校验(这里因为是初学,不涉及mybatis数据库) if ("zhangsan".equals(username) && "123".equals(password)){ //设置Session session.setAttribute("username","zhangsan"); return true; } return false; } //主页 @RequestMapping("/getUserInfo") public String getUserInfo(HttpServletRequest request){ //从Seesion中获取登录用户 HttpSession session = request.getSession(false); String userName = null; if (session!=null){ userName = (String) session.getAttribute("username"); } return userName; } }
(5)测试后端代码
①登录页
②主页
(6)修改前端代码
①使用VSCode打开并修改登录页login.html
(修改的是script部分,并且要记得保存)
<!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() { $.ajax({ url:"/user/login", type:"post", data:{ //这是一个 jQuery 代码片段 //'#'表示获取id;'.'表示获取class //因此"username":$("#userName").val()表示获取 id 为 "userName" 的输入框的值 //其中 "$" 符号是 jQuery 的简写,相当于调用 jQuery 函数 //".val()" 是 jQuery 提供的方法,用于获取或设置表单元素的值 "username":$("#userName").val(), "password":$("#password").val() }, success:function(result){ //这个result参数名字可以任取;它表示后端返回的结果 if(result){ location.href="/index.html"; //为true则跳转到主页 }else{ alert("密码错误!"); } } }); } </script> </body> </html>
②使用VSCode打开并修改主页index.html
(修改的是script部分,并且要记得保存)
<!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写在哪就在哪里被调用(比如登录页,写在login里,就是点击的时候调用ajax) $.ajax({ url:"/user/getUserInfo", type:"get", success:function(username){ $("#loginUser").text(username); } }); </script> </body> </html>
(7)效果展示
五:留言板
(1)前端代码
💗前端代码网盘链接:前端代码( 提取码:lzh7)
(2)准备工作
🌟①将留言板的前端代码引入到static目录下
🌟②运行后端并访问,确保没有错误
(3)约定前后端交互接口
1.需求分析
🌟后端需要提供两个服务
①提交留言:用户输⼊留言信息之后,后端需要把留言信息保存起来
(因为还没有学到mybatis,先保存到内存中)
②展示留言:页面展示时,需要从后端获取到所有的留言信息
2.获取所有留言的交互接口
🌟所有的留言信息,我们用List来表示,可以用JSON来描述这个List数据
3.发表新留言的交互接口
(4)后端代码
①需要先写一个MessageInfo类,用来存储留言
package com.example.demo.Controller; import lombok.Data; @Data public class MessageInfo { private String from; private String to; private String message; }
②编写后端代码
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<>(); //发表新留言的后端代码 @RequestMapping("/publish") public Boolean publishMessage(MessageInfo messageInfo){ //第一步:进行参数的校验 if (!StringUtils.hasLength(messageInfo.getFrom()) || !StringUtils.hasLength(messageInfo.getTo()) || !StringUtils.hasLength(messageInfo.getMessage())) { return false; } //第二步:添加留言到内存中,即List中 messageInfos.add(messageInfo); return true; } //查看所有留言的后端代码 @RequestMapping("/getMessageInfo") public List<MessageInfo> getMessageInfo(){ return messageInfos; } }
(5)测试后端代码
①发表新留言
②获取所有留言
(6)修改前端代码
💚修改的是script部分,记得保存
<!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(messagesList){ for(var m of messagesList){ //拼接显示留言记录 //构造节点,即拼接节点的HTML var divE = "<div>"+m.from +"对" + m.to + "说:" + m.message+"</div>"; //把节点添加到页面上 $(".container").append(divE); } } }); function submit(){ //1. 获取留言的内容 var from = $('#from').val(); var to = $('#to').val(); var say = $('#say').val(); if (from== '' || to == '' || say == '') { return; } //2.提交留言 $.ajax({ url:"/message/publish", //与后端发布新留言定义的路径要一样 type:"post", data:{ //data的属性名要和MessageInfo类的属性名一样 "from":from, "to":to, "message":say }, success:function(result){ if(result){ //result为true认为添加成功 //3. 构造节点,即拼接节点的HTML var divE = "<div>"+from +"对" + to + "说:" + say+"</div>"; //4. 把节点添加到页面上 $(".container").append(divE); //5. 清空输入框的值 $('#from').val(""); $('#to').val(""); $('#say').val(""); }else{ alert("添加留言失败"); } } }); } </script> </body> </html>
(7)效果展示
🌟即使点击了刷新,下面的留言依旧会保存
六:图书管理系统
🌟图书管理系统是相对较大的案例,现在只学了Spring Web MVC入门,因此先实现用户登录和图书列表展示的功能,后期再不断完善
(1)前端代码
💗前端代码网盘链接:前端代码( 提取码:lzh7)
(2)准备工作
🌟①将图书管理系统的前端代码引入到static目录下
🌟②运行后端并访问,确保没有错误
(1)login.html(登录页)
(2)book_list.html(图书列表展示页)
(3)约定前后端交互接口
1.需求分析
🌟后端需要提供两个服务
①用户登录:账号密码校验接口;根据输入用户名和密码校验登录是否通过
②图书列表:提供图书列表信息
2.用户登录
3.图书列表
(4)后端代码
①需要先写一个BookInfo类,用来存储图书
package com.hlizoo.book; import lombok.Data; import java.math.BigDecimal; @Data public class BookInfo { //图书ID private Integer id; //图书名字 private String bookName; //图书作者 private String author; //图书数量 private Integer count; //图书定价 private BigDecimal price; //图书出版社 private String publish; //图书借阅状态(状态这里我们用1表示可借阅,其他表示不可借阅) private Integer status; //图书借阅状态(返回中文) private String statusCN; }
②用户登录
package com.hlizoo.book; 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 userName, String password, HttpSession session){ //第一步:校验参数 if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){ return false; } //第二步:验证账户密码是否正确 if("zhangsan".equals(userName) && "123".equals(password)){ //此时说明账号密码正确,然后存Session session.setAttribute("userName",userName); return true; } return false; } }
③图书列表展示
package com.hlizoo.book; 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(){ //第一步:获取图书的数据(这里的数据是虚拟数据) List<BookInfo> bookInfos = mockData(); //第二步:对图书数据进行处理,比如说状态码返回中文 for (BookInfo bookInfo : bookInfos) { if (bookInfo.getStatus()==1){ bookInfo.setStatusCN("可借阅!"); }else { bookInfo.setStatusCN("不可借阅!"); } } //第三步:返回图书的数据 return bookInfos; } //mockData表示虚拟的数据,假的,只是用来测试 private List<BookInfo> mockData(){ //关于arraylist的优化:如果大概知道这个集合的容量,就在创建list时去初始化它的容量 List<BookInfo> bookInfos = new ArrayList<>(15); //造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; } }
(5)测试后端代码
①用户登录
②获取图书列表
(6)修改前端代码
💚修改的是script部分,记得保存
①登录页login.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> <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> function login() { $.ajax({ url:"/user/login", type:"post", data:{ "userName":$("#userName").val(), "password":$("#password").val(), }, success:function(result){ if(result){ //如果登录成功,result为true,此时跳转到图书列表页 location.href = "book_list.html"; }else{ alert("用户名或密码输入错误!"); } } }); } </script> </body> </html>
②获取图书列表页book_list.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> <!--留着这段代码是为了下面的拼接Html方便一点 --> <!-- <tr> <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td> <td>4</td> <td>大秦帝国第四册</td> <td>我是作者</td> <td>23</td> <td>33.00</td> <td>北京出版社</td> <td>可借阅</td> <td> <div class="op"> <a href="book_update.html?bookId=4">修改</a> <a href="javascript:void(0)" onclick="deleteBook(4)">删除</a> </div> </td> </tr> --> </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 finalHtml = ""; for(var book of books){ //book拿的是后端的值,因此要和BookInfo的属性名要一致 //根据每一条记录去拼接html,也就是一个<tr>标签 finalHtml += '<tr>'; finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.id+'" id="selectBook" class="book-select"></td>'; finalHtml += '<td>'+book.id+'</td>'; finalHtml += '<td>'+book.bookName+'</td>'; finalHtml += '<td>'+book.author+'</td>'; finalHtml += '<td>'+book.count+'</td>'; finalHtml += '<td>'+book.price+'</td>'; finalHtml += '<td>'+book.publish+'</td>'; finalHtml += '<td>'+book.statusCN+'</td>'; finalHtml += '<td><div class="op">'; finalHtml += '<a href="book_update.html?bookId='+book.id+'">修改</a>'; finalHtml += '<a href="javascript:void(0)" onclick="deleteBook('+book.id+')">删除</a>'; finalHtml += '</div></td></tr>'; } console.log(finalHtml); $("tbody").html(finalHtml); } }); } //翻页信息 $("#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>
(7)效果展示
七:应用分层
(1)问题引入
通过上面的练习,我们学习了Spring MVC简单功能的开发,但是我们也发现了⼀些问题
🌟问题:目前我们程序的文件和代码有点"杂乱",然而当前只是"⼀点点功能"的开发;如果我们把整个项目功能完成呢? 代码会更加的"杂乱无章"!
(2)应用分层概念
💗应用分层是⼀种软件开发设计思想
💜它将应用程序分成N个层次,这N个层次分别负责各自的职责,多个层次之间协同提供完整的功能;根据项目的复杂度,把项目分成三层,四层或者更多层
(3)应用分层作用
💗软件设计原则:高内聚低耦合
①高内聚:一个板块中各个元素之间联系的紧密程度越高越好;如果一个板块中的元素、语句、程序段等等它们的联系度越高,则内聚性越高
②低耦合:一个软件中的各个层或者模块之间依赖的关联程度越低越好,比如一个模块修改一处代码,其他模块代码的改动越少越好
💚应用分层好处总结:
(4)如何分层
💞三层架构
💗代码分层逻辑:先由Controller去调用Service,然后Service去调用Dao
①Controller(表现层):接受请求,返回结果
②Service(业务逻辑层):主要处理业务相关逻辑
③Dao(数据层):处理数据的,包含数据的存储,增删改查
💚还有一个叫model的包,我们一般放的是实体类
(5)代码重构
💛以图书管理系统的代码为例,进行代码重构
1.创建包
💗创建四个包,分别取名为Controller、Service、Dao、model
2.代码分类
①将BookController、UserController放到Controller包
②将BookInfo放到model包
3.举例代码分层
🌟我们以BookController为例,说明代码分层
4.代码分层
①在Dao包下创建一个BookDao文件,用来存放数据
🖤把BookController里的mockData方法移到BookDao去
②在Service包下创建一个BookService文件
🖤将BookController里的业务逻辑层移过去,然后进行改造
③在BookController进行代码重构
(由Controller去调用Service,然后Service去调用Dao)