🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
需求:输入两个整数,点击"点击相加"按钮,显示计算结果.
这是Web开发中的关键一环.接口又叫API,我们一般讲到的API或者接口,指的都是同一个东西.如今我们的开发一般采用前后端分离的方式,所以我们在开发之前,前端开发人员和后端开发人员会约定好前后端交互的方式.我们一般会把约定的内容写在文档上,就是"接口文档".接口文档可以理解为是应用程序中的"操作说明书".
在项目开发之前.我们需要先更具需求拟写接口文档,前后端必须都准寻接口文档中的标准.**接口文档通常由服务提供方来写,有服务使用方确认,也就是客户端.**关于接口文档怎么写,每个公司有不同的标准,一般是需求分析和接口定义(接口名称,URL),传递参数,返回参数下面我们来拟写这个案例的简单接口文档:
需求分析: 输入两个整数,点击"点击相加"按钮,显示计算结果.
接口定义:请求路径:calc/sum, 请求方式:GET/POST, 接口描述:计算两个整数相加
请求参数:
参数名 类型 是否必须 备注 num1 Integer 是 参与计算的第⼀个数 num2 Integer 是 参与计算的第⼆个数 响应数据: Content-Type: text/html 响应内容:计算机计算的结果
首先,我们需要准备前端的代码.把前端的代码calc.html放在项目的Static目录中.
Document
package com.example.demo; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/calc") @RestController public class Calc { @RequestMapping("/sum") public String sum(Integer num1,Integer num2){ Integer sum = num1 + num2; return "加和结果
" + sum; } }
首先使用查询字符串来给参数传递值来测试后端代码的正确性.后端代码的逻辑没有问题.
之后我们连带前端代码一起运行起来.
如果前后端交互的时候出现了一些问题的时候,我们一般按照下面这样的步骤来解决:
- 首先清理前端(Ctrl+f5刷新页面)缓存,和后端(pom:clean)缓存.
- 首先看错误日志
- 确认后端接口是否有问题(可以通过浏览器或者Postman访问)
- 前端请求时,Fiddler抓包或者Debug,观察接参数或者URL是否有问题.
需求:用户输入账号和密码,后端进行校验密码是否正确.
需求分析:
用户输入账号和密码,后端进行校验密码是否正确.
登录页面
接口定义:
请求路径: /user/login 请求方式: POST 接口描述: 校验账号和密码的正确性.
请求参数:
参数名 | 类型 | 是否必须 | 备注 |
---|---|---|---|
userName | String | 是 | 校验的账号 |
password | String | 是 | 校验的密码 |
响应数据:
Content-Type : text/html 响应内容: 账号密码正确:true 账号密码错误:false
主页
接口定义:
请求路径: /user/getLoginuser 请求方式: GET 接口描述: 显示当前登录用户的主页,主页上显示用户名.
请求参数:
无
响应数据:
Content-Type:text/html 响应内容: 登录的用户名.
对于前端而言,点击登录按钮的时候,需要把用户传递的信息传递到后端进行校验,后端校验成功之后,则跳转到首页:index.html,后端校验失败之后,直接弹窗.
登录页面 用户登录
用户名:
密码:
何为异步?比如我们去街道处办事,我们需要先提交我们的资料,但是给我办事的那个人不在,同步操作就是一直等,等到那个人来,异步就是先把资料留下,先回家,等事情办好之后,给你打电话.
success: function (result)
其中的success表示的是接口返回结果的成功和失败,而不是业务结果返回true或者是false.比如我们去银行办理业务,有三种可能:
- 银行没开门
- 忘记带身份证了,业务办理失败
- 证件携带齐全,业务办理成功
第一种就是接口返回了错误信息,第二种就是业务逻辑返回false,第三种就是业务逻辑返回true.
用户登录首页 登录人:
package com.example.demo; 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 Login { @RequestMapping("/login") public Boolean login(String userName, String password, HttpSession session){ //确保输入的密码和账号都不为空 //也为了保证前端传递信息成功,不会传递一个null过来 if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){ return false; } if (!"zhangsan".equals(userName) || ! "123456".equals(password)){ return false; } //密码和账号都正确,设置session session.setAttribute("userName",userName); return true; } }
其中StringUtils.hasLength()
方法是Spring中提供的一个工具方法,判断字符串是否有值.字符串为null或者是""时,返回false,其他返回true.
public static boolean hasLength(@Nullable String str) { return str != null && !str.isEmpty(); }
package com.example.demo; 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 GetUserLogin { @RequestMapping("/getUserLogin") public String getUserLogin(HttpSession httpSession){ //从session中获取用户名 String userName = (String) httpSession.getAttribute("userName"); if (StringUtils.hasLength(userName)){//确保userName有值 return userName; } return null; } }
运行代码:
登录成功:
登录失败:
需求分析:
接口定义:
GET /message/getList
响应:JSON格式[ { "from": "黑猫", "to": "白猫", "message": "喵" },{ "from": "黑狗", "to": "白狗", "message": "汪" }, //... ]
浏览器给服务器发送⼀个GET /message/getList
这样的请求,就能返回当前⼀共有哪些留言记录.结果以json的格式返回过来.POST /message/publish { "from": "黑猫", "to": "白猫", "message": "喵" } 响应:JSON格式. { ok: 1 }
我们期望浏览器给服务器发送⼀个POST /message/publish
这样的请求,就能把当前的留言提交给服务器. 留言板 留言板
输入后点击提交, 会将信息显示下方空白处
谁: 对谁: 说什么:
package com.example.demo; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; 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 MessageWall { public List messageInfoList = new ArrayList<>(); @RequestMapping("/publish") public Boolean messageController(@RequestBody MessageInfo messageInfo){ System.out.println(messageInfo);//打印日志 if (StringUtils.hasLength(messageInfo.from) && StringUtils.hasLength(messageInfo.to) && StringUtils.hasLength(messageInfo.say)){ messageInfoList.add(messageInfo); return true;//都有长度,添加成功,返回true } //添加失败,返回false return false; } @RequestMapping("/getList") public List getList(){ return messageInfoList; } }
package com.example.demo; import lombok.Data; @Data public class MessageInfo { public String from; public String to; public String say; }
Lombok是⼀个Java工具库,通过添加注解的方式,简化Java的开发.
org.projectlombok lombok true
package com.example.demo; import lombok.Data; @Data public class Person { public String name; public int age; public String sex; }
其中,@Data
注解会帮助我们自动⼀些方法,包含getter/setter,equals,toString等.
3. 更多使用方法@Data
生成的方法太多,lombok页为我们提供了一些颗粒度更细的注解.
注解 | 作用 |
---|---|
@Getter | 自动添加getter方法 |
@Setter | 自动添加setter方法 |
@ToString | 自动添加toString方法 |
@EqualsAndHashCode | 自动添加equals和hashCode方法 |
@NoArgsConstructor | 自动添加无参构造方法 |
@AllArgsConstructor | 自动添加全属性构造方法,顺序按照属性的定义顺序 |
@NonNull | 属性不能为null |
@RequiredArgsConstructor | 自动添加必需属性的构造方法,final+@NonNull的属性为必需 |
其中@Data
= @Getter+@Setter+@ToString+@NoArgsConstructor+@RequiredArgsConstructor
下面来测试运行:
[URL] POST /user/login [请求参数] name=admin&password=admin [响应] true //账号密码验证成功 false//账号密码验证失败
[URL] POST /book/getList [请求参数] ⽆ [响应] 返回图书列表 [ { "id": 1, "bookName": "活着", "author": "余华", "count": 270, "price": 20, "publish": "北京⽂艺出版社", "status": 1, "statusCN": "可借阅" }, ...
字段说明: id | 图书ID |
---|---|
bookName | 图书名称 |
author | 作者count 数量 |
price | 定价 |
publish | 图书出版社 |
status | 图书状态 1-可借阅,2-不可借阅 |
statusCN | 图书状态中文含义 |
Document
登陆
用户名 密码
图书列表展示 图书列表展示
选择 图书ID 书名 作者 数量 定价 出版社 状态 操作
package com.jrj.library; 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 Login { @RequestMapping("/login") public Boolean login(String name, String password, HttpSession session){ if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)){ return false; } if ("zhangsan".equals(name) && "123456".equals(password)){ session.setAttribute("userName",name); return true; } return false; } }
package com.jrj.library; import lombok.Data; @Data public class BookInfo {//构造一本书所有的属性 public Integer id; public String bookName; public String author; public Integer count; public Integer price; public String publish; public Integer status;//1-可借阅,2-不可借阅 public String statusCN; }
返回图书列表:package com.jrj.library; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; import java.util.Random; @RequestMapping("/book") @RestController public class BookController { @RequestMapping("/getList") public List getList(){ List list = mockData(); for (BookInfo bookInfo:list){ if (bookInfo.status == 1){ bookInfo.setStatusCN("可借阅"); }else{ bookInfo.setStatusCN("不可借阅"); } } return list; } //模拟数据 private List mockData(){ List list2 = new ArrayList<>(); for (int i = 0; i < 5; i++) { BookInfo bookInfo = new BookInfo(); bookInfo.setId(i); bookInfo.setBookName("Java编程思想"+i); bookInfo.setCount(1); bookInfo.setPublish("机械工业出版社"); bookInfo.setPrice(new Random().nextInt(100)); bookInfo.setAuthor("高斯林"); bookInfo.setStatus(1); list2.add(bookInfo); } return list2; } }
测试运行:
登录页面正常
可正常登录,图书列表页面展示正确.
通过上面的几个案例,我们看到我们的代码平铺在我们的项目中,显得非常杂乱.所以我们要使用应用分层.
常见的应用分层结构如下:
我们之前提到的"MVC",就是把整体的系统分成了Model(模型),View(视图)和Controller(控制器)三个层次.现在我们主流开发的方式是"前后端分离"的方式,后端开发不再需要关心前端的实现,所以对java后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层和数据层.这种分层方式也称之为"三层架构".
在我们创建Spring项目中,具体对分层的实现就是创建一个一个不同的目录,把代码分层管理起来.其中不同层面的目录一般用以下的命名方式:
• Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
• Service:业务逻辑层。处理具体的业务逻辑。
• Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查.
• Model: 用于存储对实物属性的描述
下面我们对之前的图书管理的代码进行拆分重构:
package com.jrj.library.controller; import com.jrj.library.service.LoginService; import jakarta.servlet.http.HttpSession; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/user") @RestController public class LoginController { @RequestMapping("/login") public boolean login(String name, String password, HttpSession session){ LoginService loginService = new LoginService(); return loginService.login(name,password,session); } }
package com.jrj.library.controller; import com.jrj.library.BookInfo; import com.jrj.library.service.BookService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RequestMapping("/book") @RestController public class BookController { BookService bookService = new BookService(); @RequestMapping("/getList") public List getList(){ List bookInfos = new ArrayList<>(); bookInfos = bookService.getList(); return bookInfos; } }
package com.jrj.library.service; import com.jrj.library.BookInfo; import com.jrj.library.dao.Data; import java.util.List; public class BookService { public List getList(){ Data data = new Data(); List list = data.mockData(); for (BookInfo bookInfo:list){ if (bookInfo.status == 1){ bookInfo.setStatusCN("可借阅"); }else{ bookInfo.setStatusCN("不可借阅"); } } return list; } }
package com.jrj.library.service; import jakarta.servlet.http.HttpSession; import org.springframework.util.StringUtils; public class LoginService { public Boolean login(String name, String password, HttpSession session){ if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)){ return false; } if ("zhangsan".equals(name) && "123456".equals(password)){ session.setAttribute("userName",name); return true; } return false; } }
import java.util.Random; public class Data { //向业务逻辑端提供数据 public List mockData(){ List list2 = new ArrayList<>(); for (int i = 0; i < 5; i++) { BookInfo bookInfo = new BookInfo(); bookInfo.setId(i); bookInfo.setBookName("Java编程思想"+i); bookInfo.setCount(1); bookInfo.setPublish("机械工业出版社"); bookInfo.setPrice(new Random().nextInt(100)); bookInfo.setAuthor("高斯林"); bookInfo.setStatus(1); list2.add(bookInfo); } return list2; } }
package com.jrj.library.model; import lombok.Data; @Data public class BookInfo {//构造一本书所有的属性 public Integer id; public String bookName; public String author; public Integer count; public Integer price; public String publish; public Integer status;//1-可借阅,2-不可借阅 public String statusCN; }
上面的"三层架构",遵循了一种软件设计的原则,叫做"高内聚,低耦合".
高内聚指的是⼀个模块中各个元素之间的联系比较紧密.如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即"高内聚".
低耦合指的是软件中各个层、模块之间的依赖关联程序越低越好。修改⼀处代码,其他模块的代码改动越少越好.
前面提到的MVC架构模式和三层架构模式有什么区别?
MVC架构模式由三部分组成,分别是:模型(Model),视图(View)和控制器(Controller).
三层架构将业务应用划分为:表现层,业务逻辑层,数据访问层.
MVC模式强调数据和视图分离,将数据展示和数据处理分开,通过控制器对两者进行组合
三层架构强调不同维度数据处理的高内聚和低耦合,将交互界面,业务处理和数据库操作的逻辑分开.