写在前面的话:
本篇博文是本人(初级程序员)基于企业级实战项目后做一点点的记录,一是为了总结自己的开发经历,二是为了将自己所遇到的问题和所解决的办法分享给刚刚入门的各位小伙伴。有实战经验的各位前辈们可以将本人写的内容看成是饭后甜点,不到之处还请批评指正哦!
后端接口流程图
不知道各位小伙伴拿到自己的第一个实战项目的心情是如何的?是兴奋,是紧张,还是茫然?
如上图所示,为此篇文章的核心思想的体现!这是“同事”+“同学”+“自己”在项目中的所思所学所得。
此次的内容我将在我之前的文章SpringBoot_实现基本增删改查(前后端分离版)基础之上将自己的相关理解写得更加透彻一些,因为8月份我刚接触这个框架,很多还是混淆的状态,只知道要这样做,但是不知道为什么要这样做,这此我就来讲讲为什么要这样做。
思想归思想,思想流程是如上图这样,但是具体的编码过程应当按以下的步骤来梳理,逻辑才会更加的清晰易懂!
(1)建立实体类;
(2)Service接口;
(3)XML文件;
(4)Mapper接口;
(5)ServiceImpl实现类;
(6)Controller层调用接口;
(7)最终接口展示。
1. 建立实体类
实体类是连通着我们的前端接口内容,返回的响应参数都是来自于这儿,参数的数据类型和注释都是来源于这里,所以建立实体类作为我们刚开始编码的第一步毋庸置疑。建立的同时也能知道我们最终是要得到什么值返回给前端,这样我们后面的步骤就围绕着如何得到我们的实体类而行动。
这里想说明的是,因为很多时候返回给前端是一个集合类型,存储着很多的数据。这个时候,我们一般是建立两个实体类,一个实体类用来存储List<>集合,另外一个是用来存储该集合里的内部数据。具体的代码实例如下所示:
其中,我们每个类之前加上一个“@Data”注解,能给我们省去不少的工作量。
@Data注解的作用有二:(1)为当前类提供读写方法,从而不用写get、set方法;(2)为当前类提供 equals()、hashCode()、toString() 方法
@ApiModelProperty注解: 是对各个属性的说明,加上这一注解可以在Swagger中看到响应参数的相关注释内容,让前端工作者能第一时间获取到数据信息提示,提供方便。
package com.hncr.system.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* @ClassName: UserVo
* @Description:用户集合实体类
* @Author: YuHao
* @Date: 2021/12/15
*/
@Data
public class UserVo {
@ApiModelProperty("用户集合")
private List<UserListVo> userListVos;
}
package com.hncr.system.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @ClassName: UserListVo
* @Description:用户类
* @Author: YuHao
* @Date: 2021/12/15
*/
@Data
public class UserListVo {
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("年龄")
private Integer age;
@ApiModelProperty("性别:(0:男,1:女)")
private Integer sex;
}
2. Service接口
完成第一步以后,我们开始写第二个部分。所谓的基于“接口编程”思想大概就是基于Service接口进行的吧!返回类型是我们的UserVo实体类,因为最终要得到的值就是要返回一个集合给前端,所以这里的返回类型便是我们的实体类。(以前的疑问,在这次项目中解决了)具体的代码实例如下所示:
package com.hncr.system.service;
import com.hncr.system.domain.vo.UserVo;
/**
* @InterfaceName: IUserService
* @Description: Service接口
* @Author: YuHao
* @Date: 2021/12/15
*/
public interface IUserService {
UserVo userSummary();
}
3. XML文件
其实第三步和第四步是同时进行,之所以将XML文件先写,是因为我们需要将我们的sql语句写好(这个很重要),确定具体需要多少个sql语句才能解决具体的Service接口问题。具体问题具体分析,有时候写一个sql语句就搞定问题了,有时候也需要三四个才行。逻辑不清晰的时候建议去咨询一下前辈或者需求的同事,他们一般都能将你的疑问给解决掉。之前我也发几篇基础版的基于mysql写基本增删查改的sql语句,但是应付项目上的操作还是差点意思,因为牵扯到很多业务逻辑的时候,基本的几个就不够看了。(后续本人将单独增加专说sql实用性语句的博文,大约在冬季哦)
在Navicat中做一些实例操作:根据年龄进行降序排序
SELECT
name,
age,
sex
FROM
`user`
ORDER BY age DESC
(其实在实际项目中要查询或者改变的操作会很多,数据量也会很大,很多时候要对某些个字段做索引操作才会缩短查询的时间,特别说明一下,查询时间超过3秒或者4秒的sql,在前端看来就是一个相当慢的接口了。数据量大的数据库,我们尽量做到只访问一次就好,以避免浪费不必要的时间来影响用户的体验)
在Navicat工具中我们已经查询得到我们想要的效果了,那我们要加入到我们的XML文件中,结合我们的第四步配置namespace、id、resultType等。实例代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hncr.system.mapper.UserMapper">
<select id="userSummaryMapper" resultType="java.util.Map">
SELECT
name,
age,
sex
FROM
`user`
ORDER BY age DESC
</select>
</mapper>
4. Mapper接口
其实在第三步过程中写好我们的sql语句后,就应该跳转到第四步当中了,因为具体的命名空间等是要以Mapper接口来命名的。在上面可知用一条sql语句就搞定了想要的结果,那Mapper接口写一个即可。反正就是sql写了几个,我们对应的就得写几个Mapper接口,其实例代码如下所示:
可以看到这里的返回类型不太一样了哦,因为我之前的文章中是写的List < User >,而在这里是写的List < Map >,区别在于XML文件中的resultType中写明的为:“java.util.Map”,而我上篇文章中对应的resultType中写明的为:“com.example.demo.entity.User”,所以要XML文件中的resultType要和Mapper接口中的一一对应上,否则是查询不到数据,接口报错。在实际的项目中,后续的这种接口方式用得较多,List、Map、List < Map >可以对其做一下了解,看一下就知道如何使用。集合很重要,项目中大多数都是使用了集合,因为太好用了,大家一定掌握好。id对应上我们的Mapper接口名字 “userSUmmaryMapper”。
记住一点就好:XML文件多少个sql,Mapper接口就写多少个;Mapper接口返回类型,接口名称要与XML文件中resultType、id等要一一对应上。遵循这一点,多练习几次也就不会有程序报错了(刚刚开始练手,难免犯错)。
package com.hncr.system.mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
/**
* @InterfaceName: UserService
* @Description:Mapper接口
* @Author: YuHao
* @Date: 2021/12/15
*/
@Repository
public interface UserMapper {
List<Map> userSummaryMapper();
}
5. ServiceImpl实现类
到了第五步,这可以算是当前所写后端接口中至关重要的一步了,因为在这里要实现数据的交互,代码逻辑。这里分别对应着流程图中的三个环节 “最终值、中间值、层层计算”,如果我们通过sql语句就能得到我们想要的结果,这个结果称之为 “最终值”,如果通过sql语句只是得到我们中间环节的某些值,这些值我们称之为 “中间值”,需要通过我们在实现类中用相关的逻辑代码(业务代码)进行 “层层计算”。直接能够得到最终值的是最为简单的过程,从数据库拿到数据就可以用,这个接口做起来会非常地快,因为压根就不需要用脑子呀,拿到数据直接 “Set” 就完事了。但是如果所做的项目相对而言逻辑复杂一点的,那么很多时候从数据库拿到的都是原始数据作为我们的中间值,这些数据是要通过我们的 “通用/共用/公用方法” 层层计算才能得到想要的最终值。不管如何,最终都是要Set到我们的对象之中,直接上所对应的实例代码,这样看起来直观明了:
package com.hncr.system.service.impl;
import com.hncr.system.domain.vo.UserListVo;
import com.hncr.system.domain.vo.UserVo;
import com.hncr.system.mapper.UserMapper;
import com.hncr.system.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @ClassName: UserServiceImpl
* @Description:
* @Author: YuHao
* @Date: 2021/12/15
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
List<Map> userSummaryMapper(){
return userMapper.userSummaryMapper();
}
public UserVo userSummary(){
UserVo userVo = new UserVo();
List<Map> list1 = userSummaryMapper();
List<UserListVo> list2 = new ArrayList<>();
for (int i = 0; i < list1.size(); i++) {
UserListVo userListVo = new UserListVo();
Map map = list1.get(i);
userListVo.setName((String) map.get("name"));
userListVo.setAge((Integer) map.get("age"));
userListVo.setSex((Integer) map.get("sex"));
list2.add(userListVo);
}
userVo.setUserListVos(list2);
return userVo;
}
}
以上的代码获取到是最终值,是直接Set到对象之中的。上面的实例代码算是最简单的了,提醒刚入门的小伙伴们,这种方式都是一些较常规做法,是需要掌握的。如果这里出来的是中间值,也不需要怕什么,所谓的 “层层计算” 这些什么的我们都是在大学期间或多或少接触过,我们不管是用什么语言编程都是做过的,不外乎就是“for、if、while、简单的常规算法…”,这个时候让我想到大学为啥那时候要学习数据结构了,因为不管你是码农也好还是算法工程师也罢,把这个学好都是会有用武之地的!!!(当然了,我是学渣,我就没学好,┭┮﹏┭┮)
6.Controller层调用接口
写完前五步的内容,第六步算是结尾工作了,跟以前的做法类似,这里就是一个调用过程,将相关的注解写好,注入相关的接口方法,然后返回给前端,就完成了我们的一个接口,记住了,这才是仅仅的一个而已,还有更多其他类似或者复杂一丢丢的接口在等着你去写哦~~照惯例上我们的实例代码:
package com.hncr.web.controller.system;
import com.hncr.system.domain.JsonResult;
import com.hncr.system.domain.vo.UserVo;
import com.hncr.system.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName: UserController
* @Description:
* @Author: YuHao
* @Date: 2021/12/15
*/
@RestController
@Api(value = "用户信息", tags = "用户信息")
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@ApiOperation(value = "用户信息汇总", notes = "{<br/>" +
" \"msg\": \"操作成功\",<br/>" +
" \"code\": 200,<br/>" +
" \"data\": {<br/>" +
" }<br/>" +
"}")
@GetMapping("UserSummary")
public JsonResult<UserVo> userSummary(){
UserVo userVo = userService.userSummary();
return JsonResult.success(userVo);
}
}
以上的Controller层代码中,很多我都在以前都做过介绍了,这里我就不再累赘了,就是几个非常常用的注解,不记得就多回头看看自己或者同事以前写过的项目代码,还有直接copy使用也可以。主要想说的是,本人这有一个不错的json返回值类,我也将其贴出来,大家可以拿这个用,这是已离职的好朋友分享给我的,其实他技术挺强,但是因为学历限制了他的发展,确实可惜了。这就说明了其实企业还是比较看重学历的,有一个好一点的学历,起步工资还是高不少的!!!加油吧,小伙伴们,下面是具体的代码:
package com.hncr.system.domain;
import com.alibaba.fastjson.JSONObject;
import com.hncr.common.constant.HttpStatus;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @ClassName: JsonResult
* @Description: json 返回值
* @Author: YuHao
* @Date: 2021/11/25
*/
@Data
public class JsonResult<T>{
@ApiModelProperty(notes = "响应码")
private Integer code;
@ApiModelProperty(notes = "响应消息")
private String msg;
@ApiModelProperty(notes = "返回数据")
private T data;
/**
* 若没有数据返回,默认状态码为 0,提示信息为“操作成功!”
*/
public JsonResult() {
this.code = HttpStatus.SUCCESS;
this.msg = "success";
}
/**
* 若没有数据返回,可以人为指定状态码和提示信息
* @param code
* @param msg
*/
public JsonResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 有数据返回时,状态码为 0,默认提示信息为“操作成功!”
* @param data
*/
public JsonResult(T data) {
this.data = data;
this.code = HttpStatus.SUCCESS;
this.msg = "success";
}
public static JsonResult success(){
return new JsonResult<>();
}
public static <T> JsonResult success(T data){
return new JsonResult(data);
}
public static JsonResult error(String msg){
return new JsonResult(HttpStatus.ERROR,msg);
}
public static JsonResult error(){
return new JsonResult(HttpStatus.ERROR,"error");
}
public static JsonResult error(int code, String msg){
return new JsonResult(code,msg);
}
public static JsonResultBuilder builder(){
return new JsonResultBuilder();
}
public static class JsonResultBuilder{
private JSONObject buildData = new JSONObject();
public JsonResultBuilder put(String key,Object value){
buildData.put(key,value);
return this;
}
public JsonResult get(){
return JsonResult.success(buildData);
}
}
}
7.最终接口展示
最终访问我们的本地地址+“#doc.html”,在Swagger中可以看到我们的接口已经写好了,展示的效果也是按照年龄降序排列的集合。这样的接口,前端的同事拿来就可以用,响应参数、参数数据类型都是非常的清晰。此处我是在已有的项目上做的演示实例,所以Swagger都是已经配置好的,所以此处有是瑕疵的。如果没有配置Swagger的小伙伴应该还需要先百度一下如何配置好Swagger,下个阶段我有时间也整理一下如何配置Swagger。讲实话没有Swagger看不到最终的前端调用效果确实挺难受的,所以这个环节是在我们在写接口之前就要配置好。(这里我写得有点累了,换下次再写吧,O(∩_∩)O哈哈~)
综上所述,这便是基于SpringBoot后端接口的相关流程,有相关的思想也有编写的步骤。有些思想或者步骤我可能写得还不够细,还比较粗糙,但是大致地想法还是表达到位了。我的初衷就是想对于刚入门的小伙伴还是有一些小小的帮助的就好,自己也算是对我的第一个实战项目做大致的总结。最终项目告一段落,界面接口完成了,Bug也改了不少,新增需求也做了一些改善,后续的代码也做了一些优化。成长了不少,也收获了不少,当然也加了很多很多的班…
不管如何,大家还是要参与到实际的项目中,才会逐步收获到一些从来没有遇到过的“惊喜”!!!
编写不易,路过的朋友,如果博客内容对你有所帮助的话,希望能一键三连一下呀,谢谢支持哦!!!